| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * squirrelmail_rpc.php |
| 5 | * |
| 6 | * This file contains the entry point to the "SquirrelMail API" -- the |
| 7 | * remote procedure call request receiver. |
| 8 | * |
| 9 | * RPC requests are currently understood as simple HTTP GET or POST |
| 10 | * requests. The SquirrelMail default_rpc template set responds in a |
| 11 | * SOAP (currently v1.2) compliant manner, but this interface does not |
| 12 | * (yet?) understand SOAP requests. The format of responses can be |
| 13 | * changed by creating a different RPC template set and pointing to it |
| 14 | * with $rpc_templateset in the main SquirrelMail configuration file. |
| 15 | * |
| 16 | * @copyright 1999-2011 The SquirrelMail Project Team |
| 17 | * @license http://opensource.org/licenses/gpl-license.php GNU Public License |
| 18 | * @version $Id$ |
| 19 | * @package squirrelmail |
| 20 | * @since 1.5.2 |
| 21 | * |
| 22 | */ |
| 23 | |
| 24 | /** This is the squirrelmail_rpc page */ |
| 25 | define('PAGE_NAME', 'squirrelmail_rpc'); |
| 26 | |
| 27 | //FIXME: If we decide to route ALL requests, even normal page |
| 28 | // requests through this file, need to change page requests |
| 29 | // to something like this |
| 30 | //http://example.org/squirrelmail/src/squirrelmail_rpc.php?page=read_body&passed_id=47633... |
| 31 | // This file would then add ".php" to the "page" variable |
| 32 | // and pass the request on to that page by simply require()ing |
| 33 | // that page and exiting. |
| 34 | // Does this present problems, security or otherwise? What |
| 35 | // problems are created by the fact that the page request |
| 36 | // is always the same thing (some parts of the code and some |
| 37 | // plugins switch functionality based on $PHP_SELF and other |
| 38 | // $_SERVER variables that look for specific page names -- those |
| 39 | // can be fixed by looking at the "page" GET argument, but what |
| 40 | // other issues are created)? What about plugins? How would |
| 41 | // they work in this scheme? Would they be a lot more difficult |
| 42 | // to develop? |
| 43 | //NOTE: It is not entirely clear if doing the above is even desirable. |
| 44 | // Initial conversations on the squirrelmail-devel list were |
| 45 | // inconclusive. On one hand, doing so would give us one master |
| 46 | // file that handles any and all incoming requests, no matter |
| 47 | // where they came from or what format/type they are. On the |
| 48 | // other, keeping page requests out of this file keeps this file |
| 49 | // lean and specific to one technology: our RPC interface. |
| 50 | |
| 51 | |
| 52 | /** |
| 53 | * Include the SquirrelMail initialization file. |
| 54 | */ |
| 55 | //FIXME: init.php assumes it is being called by a browser, so some error |
| 56 | // conditions are handled by immediately calling error_box() or |
| 57 | // otherwise trying to push something to the browser, which should |
| 58 | // be avoided at all costs. This is also pervasive in the whole |
| 59 | // core and must be cleaned up entirely before this can be a very |
| 60 | // functional RPC interface |
| 61 | require('../include/init.php'); |
| 62 | |
| 63 | |
| 64 | |
| 65 | //FIXME: do we need to put this list somewhere else? |
| 66 | //FIXME: do we want to use constants instead? probably not a bad idea, although plugins probably won't, so we still want to try to keep track of the plugin error codes too if possible (new plugin website should help) |
| 67 | /** |
| 68 | * Known core error codes: |
| 69 | * |
| 70 | * 1 - No RPC action was given in request (please use "rpc_action") |
| 71 | * 2 - RPC action was not understood (perhaps a needed plugin is |
| 72 | * not installed and activated?) |
| 73 | * |
| 74 | * Known plugin error codes: |
| 75 | * |
| 76 | * 500 - Empty Folders plugin empty_folders_purge_trash action failed |
| 77 | * 501 - Empty Folders plugin empty_folders_purge_all action failed |
| 78 | * 502 - Empty Folders plugin empty_folders_delete_all action failed |
| 79 | * 503 - Mark Read plugin mark_read_read_all action failed |
| 80 | * 504 - Mark Read plugin mark_read_unread_all action failed |
| 81 | * |
| 82 | */ |
| 83 | |
| 84 | |
| 85 | |
| 86 | /** |
| 87 | * Get RPC Action (can be in either GET or POST) |
| 88 | * |
| 89 | */ |
| 90 | if (!sqGetGlobalVar('rpc_action', $rpc_action, SQ_FORM)) { |
| 91 | sm_rpc_return_error('', 1, _("No RPC action given"), 'client', 400, 'Bad Request'); |
| 92 | } |
| 93 | |
| 94 | |
| 95 | |
| 96 | /** |
| 97 | * No matter what our response is, the headers |
| 98 | * will not change. |
| 99 | * |
| 100 | */ |
| 101 | $oTemplate->header('Content-Type: text/xml'); |
| 102 | $oTemplate->header('Content-Type: application/xml'); // required by IE |
| 103 | $oTemplate->header('Pragma: no-cache'); |
| 104 | $oTemplate->header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0'); |
| 105 | $oTemplate->header('Expires: Sat, 1 Jan 2000 00:00:00 GMT'); |
| 106 | //TODO: is this needed? $oTemplate->header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); |
| 107 | |
| 108 | |
| 109 | |
| 110 | /** |
| 111 | * Allow plugins to add their own RPC action |
| 112 | * or modify behavior of SM core RPC actions... |
| 113 | * |
| 114 | * A plugin that handles a custom RPC action must |
| 115 | * return TRUE to the hook so that it knows that |
| 116 | * the action was handled and was not an unknown |
| 117 | * action. If the action was not handled, the plugin |
| 118 | * should return FALSE to the hook. |
| 119 | * |
| 120 | * Developer note: the $rpc_action parameter is passed |
| 121 | * in an array in case we can think of more parameters |
| 122 | * to add in the future. |
| 123 | * |
| 124 | * Known users of this hook: |
| 125 | * empty_folders |
| 126 | * mark_read |
| 127 | * |
| 128 | */ |
| 129 | $temp = array(&$rpc_action); |
| 130 | $handled_by_plugin = boolean_hook_function('squirrelmail_rpc', $temp, 1); |
| 131 | |
| 132 | |
| 133 | |
| 134 | /** |
| 135 | * Go take care of each RPC action (unless plugin already did) |
| 136 | * |
| 137 | */ |
| 138 | if (!$handled_by_plugin) switch (strtolower($rpc_action)) { |
| 139 | |
| 140 | /** |
| 141 | * Delete Messages |
| 142 | * |
| 143 | */ |
| 144 | case 'delete_messages': |
| 145 | |
| 146 | require_once(SM_PATH . 'functions/mailbox_display.php'); |
| 147 | require_once(SM_PATH . 'functions/imap.php'); |
| 148 | |
| 149 | if (!sqGetGlobalVar('delete_ids', $delete_ids, SQ_FORM)) { |
| 150 | sm_rpc_return_error($rpc_action, 99, _("No deletion ID given"), 'client', 400, 'Bad Request'); |
| 151 | } |
| 152 | $delete_ids = explode(',', $delete_ids); |
| 153 | if (!sqGetGlobalVar('mailbox', $mailbox, SQ_FORM)) { |
| 154 | sm_rpc_return_error($rpc_action, 99, _("No mailbox given"), 'client', 400, 'Bad Request'); |
| 155 | } |
| 156 | if (sqGetGlobalVar('startMessage', $startMessage, SQ_INORDER, 1)) { |
| 157 | $startMessage = (int) $startMessage; |
| 158 | } |
| 159 | sqGetGlobalVar('what', $what, SQ_FORM, 0); |
| 160 | if (sqGetGlobalVar('account', $iAccount, SQ_GET, 0)) { |
| 161 | $iAccount = (int) $iAccount; |
| 162 | } |
| 163 | //FIXME: need to grab the bypass trash variable here too! probably other vars... |
| 164 | |
| 165 | /* FIXME: --- The following code was just experimental/proof-of-concept; the rest |
| 166 | of the implementation of this functionality still needs to be done "for real" |
| 167 | $oImapMessage = new IMAP_Message(0, $mailbox, $startMessage, $what, $iAccount); |
| 168 | foreach ($delete_ids as $id) { |
| 169 | $oImapMessage->setUid($id); |
| 170 | //FIXME: establish constants for $hide values (the 3 below indicates not to show errors, but to return any error string) |
| 171 | $result = $oImapMessage->deleteMessage(3); |
| 172 | if ($result !== TRUE) { |
| 173 | sm_rpc_return_error($rpc_action, 99, $result, 'server', 500, 'Server Error'); |
| 174 | } |
| 175 | } |
| 176 | --- */ |
| 177 | |
| 178 | sm_rpc_return_success(); |
| 179 | //FIXME: Just for testing the line above can be changed to something like this: |
| 180 | //sm_rpc_return_success($rpc_action, 0, 'Hooray! Message(s) deleted. Refresh your message list and make sure.'); |
| 181 | break; |
| 182 | |
| 183 | |
| 184 | /** |
| 185 | * Default: error out |
| 186 | * |
| 187 | */ |
| 188 | default: |
| 189 | sm_rpc_return_error($rpc_action, 2, _("RPC action not understood"), 'client', 400, 'Bad Request'); |
| 190 | break; |
| 191 | |
| 192 | } |
| 193 | |
| 194 | |
| 195 | |
| 196 | /** |
| 197 | * Returns an error message to the RPC caller and exits |
| 198 | * |
| 199 | * NOTE that this function exits and will never return |
| 200 | * |
| 201 | * @param string $rpc_action The RPC action that is being handled |
| 202 | * (OPTIONAL; default attempt to grab from GET/POST) |
| 203 | * @param int $error_code The (application-level) error code for the current |
| 204 | * error condition |
| 205 | * @param string $error_text Any error message associated with the error |
| 206 | * condition (OPTIONAL; default empty string) |
| 207 | * @param string $guilty_party A string indicating the party who caused the |
| 208 | * error: either "client" or "server" (OPTIONAL; |
| 209 | * default unspecified) |
| 210 | * @param int $http_status_code When non-zero, this value will be sent to |
| 211 | * the browser in the HTTP headers as the request |
| 212 | * status code (OPTIONAL; default not used) |
| 213 | * @param string $http_status_text A string naming the HTTP status, usually the |
| 214 | * title of the corresponding status code as |
| 215 | * found on: |
| 216 | * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
| 217 | * (OPTIONAL; default not used; $http_status_code |
| 218 | * must also be provided). |
| 219 | * |
| 220 | */ |
| 221 | function sm_rpc_return_error($rpc_action=NULL, $error_code, |
| 222 | $error_text='', $guilty_party='', |
| 223 | $http_status_code=0, $http_status_text='') { |
| 224 | |
| 225 | global $oTemplate; |
| 226 | |
| 227 | if (is_null($rpc_action)) sqGetGlobalVar('rpc_action', $rpc_action, SQ_FORM); |
| 228 | |
| 229 | if ($http_status_code) { |
| 230 | $oTemplate->header('HTTP/1.1 ' . $http_status_code . ' ' . $http_status_text); |
| 231 | $oTemplate->header('Status: ' . $http_status_code . ' ' . $http_status_text); |
| 232 | } |
| 233 | |
| 234 | $oTemplate->assign('rpc_action', $rpc_action); |
| 235 | $oTemplate->assign('error_code', $error_code); |
| 236 | $oTemplate->assign('error_text', $error_text); |
| 237 | $oTemplate->assign('guilty_party', $guilty_party); |
| 238 | |
| 239 | $oTemplate->display('rpc_response_error.tpl'); |
| 240 | |
| 241 | exit; |
| 242 | |
| 243 | } |
| 244 | |
| 245 | |
| 246 | |
| 247 | /** |
| 248 | * Returns a standard success result to the RPC caller and exits |
| 249 | * |
| 250 | * NOTE that this function exits and will never return |
| 251 | * |
| 252 | * @param string $rpc_action The RPC action that is being handled |
| 253 | * (OPTIONAL; default attempt to grab from GET/POST) |
| 254 | * @param int $result_code The result code (OPTIONAL; default 0) |
| 255 | * @param string $result_text Any result message (OPTIONAL; default |
| 256 | * empty string) |
| 257 | * |
| 258 | */ |
| 259 | function sm_rpc_return_success($rpc_action=NULL, $result_code=0, $result_text='') { |
| 260 | |
| 261 | if (is_null($rpc_action)) sqGetGlobalVar('rpc_action', $rpc_action, SQ_FORM); |
| 262 | |
| 263 | global $oTemplate; |
| 264 | $oTemplate->assign('rpc_action', $rpc_action); |
| 265 | $oTemplate->assign('result_code', $result_code); |
| 266 | $oTemplate->assign('result_text', $result_text); |
| 267 | |
| 268 | $oTemplate->display('rpc_response_success.tpl'); |
| 269 | |
| 270 | exit; |
| 271 | |
| 272 | } |
| 273 | |
| 274 | |
| 275 | |