PageRenderTime 73ms CodeModel.GetById 3ms app.highlight 58ms RepoModel.GetById 2ms app.codeStats 0ms

/class.contextio.php

http://github.com/contextio/PHP-ContextIO
PHP | 1549 lines | 1173 code | 104 blank | 272 comment | 373 complexity | 7983c47f091633a1dd502a59f238ea23 MD5 | raw file
   1<?php
   2/*
   3Copyright (C) 2011 DokDok Inc.
   4
   5Permission is hereby granted, free of charge, to any person obtaining a copy
   6of this software and associated documentation files (the "Software"), to deal
   7in the Software without restriction, including without limitation the rights
   8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   9copies of the Software, and to permit persons to whom the Software is
  10furnished to do so, subject to the following conditions:
  11
  12The above copyright notice and this permission notice shall be included in
  13all copies or substantial portions of the Software.
  14
  15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21THE SOFTWARE.
  22*/
  23
  24/**
  25 * Context.IO API client library
  26 * @copyright Copyright (C) 2011 DokDok Inc.
  27 * @licence http://opensource.org/licenses/mit-license MIT Licence
  28 */
  29
  30require_once dirname(__FILE__) . '/class.contextioresponse.php';
  31require_once dirname(__FILE__) . '/OAuth.php';
  32
  33/**
  34 * Class to manage Context.IO API access
  35 */
  36class ContextIO {
  37
  38	protected $responseHeaders;
  39	protected $requestHeaders;
  40	protected $oauthKey;
  41	protected $oauthSecret;
  42	protected $saveHeaders;
  43	protected $ssl;
  44	protected $endPoint;
  45	protected $apiVersion;
  46	protected $lastResponse;
  47	protected $authHeaders;
  48
  49	/**
  50	 * Instantiate a new ContextIO object. Your OAuth consumer key and secret can be
  51	 * found under the "settings" tab of the developer console (https://console.context.io/#settings)
  52	 * @param $key Your Context.IO OAuth consumer key
  53	 * @param $secret Your Context.IO OAuth consumer secret
  54	 */
  55	function __construct($key, $secret) {
  56		$this->oauthKey = $key;
  57		$this->oauthSecret = $secret;
  58		$this->saveHeaders = false;
  59		$this->ssl = true;
  60		$this->endPoint = 'api.context.io';
  61		$this->apiVersion = '2.0';
  62		$this->lastResponse = null;
  63		$this->authHeaders = true;
  64	}
  65
  66	/**
  67	 * Attempts to discover IMAP settings for a given email address
  68	 * @link http://context.io/docs/2.0/discovery
  69	 * @param mixed $params either a string or assoc array
  70	 *    with email as its key
  71	 * @return ContextIOResponse
  72	 */
  73	public function discovery($params) {
  74		if (is_string($params)) {
  75			$params = array('email' => $params);
  76		}
  77		else {
  78			$params = $this->_filterParams($params, array('email'), array('email'));
  79			if ($params === false) {
  80				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
  81			}
  82		}
  83		return $this->get(null, 'discovery?source_type=imap&email=' . $params['email']);
  84	}
  85
  86	/**
  87	 *
  88	 * @link http://context.io/docs/2.0/connecttokens
  89	 */
  90	public function listConnectTokens($account=null) {
  91		return $this->get($account, 'connect_tokens');
  92	}
  93
  94	/**
  95	 *
  96	 * @link http://context.io/docs/2.0/connecttokens
  97	 */
  98	public function getConnectToken($account=null,$params) {
  99		if (is_string($params)) {
 100			$params = array('token' => $params);
 101		}
 102		else {
 103			$params = $this->_filterParams($params, array('token'));
 104			if ($params === false) {
 105				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 106			}
 107		}
 108		return $this->get($account, 'connect_tokens/' . $params['token']);
 109	}
 110
 111	/**
 112	 *
 113	 * @link http://context.io/docs/2.0/connecttokens
 114	 */
 115	public function addConnectToken($account=null,$params=array()) {
 116		$params = $this->_filterParams($params, array('service_level','email','callback_url','first_name','last_name'), array('callback_url'));
 117		if ($params === false) {
 118			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 119		}
 120		return $this->post($account, 'connect_tokens', $params);
 121	}
 122
 123	/**
 124	 *
 125	 * @link http://context.io/docs/2.0/connecttokens
 126	 */
 127	public function deleteConnectToken($account=null, $params) {
 128		if (is_string($params)) {
 129			$params = array('token' => $params);
 130		}
 131		else {
 132			$params = $this->_filterParams($params, array('token'), array('token'));
 133			if ($params === false) {
 134				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 135			}
 136		}
 137		return $this->delete($account, 'connect_tokens/' . $params['token']);
 138	}
 139
 140	/**
 141	 *
 142	 * @link http://context.io/docs/2.0/oauthproviders
 143	 */
 144	public function listOAuthProviders() {
 145		return $this->get(null, 'oauth_providers');
 146	}
 147
 148	/**
 149	 *
 150	 * @link http://context.io/docs/2.0/oauthproviders
 151	 */
 152	public function getOAuthProvider($params) {
 153		if (is_string($params)) {
 154			$params = array('provider_consumer_key' => $params);
 155		}
 156		else {
 157			$params = $this->_filterParams($params, array('provider_consumer_key'), array('provider_consumer_key'));
 158			if ($params === false) {
 159				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 160			}
 161		}
 162		return $this->get(null, 'oauth_providers/' . $params['provider_consumer_key']);
 163	}
 164
 165	/**
 166	 *
 167	 * @link http://context.io/docs/2.0/oauthproviders
 168	 */
 169	public function addOAuthProvider($params=array()) {
 170		$params = $this->_filterParams($params, array('type','provider_consumer_key','provider_consumer_secret'), array('type','provider_consumer_key','provider_consumer_secret'));
 171		if ($params === false) {
 172			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 173		}
 174		return $this->post(null, 'oauth_providers', $params);
 175	}
 176
 177	/**
 178	 *
 179	 * @link http://context.io/docs/2.0/oauthproviders
 180	 */
 181	public function deleteOAuthProvider($params) {
 182		if (is_string($params)) {
 183			$params = array('provider_consumer_key' => $params);
 184		}
 185		else {
 186			$params = $this->_filterParams($params, array('provider_consumer_key'), array('provider_consumer_key'));
 187			if ($params === false) {
 188				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 189			}
 190		}
 191		return $this->delete(null, 'oauth_providers/' . $params['provider_consumer_key']);
 192	}
 193
 194	/**
 195	 * Returns the 20 contacts with whom the most emails were exchanged.
 196	 * @link http://context.io/docs/2.0/accounts/contacts
 197	 * @param string $account accountId of the mailbox you want to query
 198	 * @return ContextIOResponse
 199	 */
 200	public function listContacts($account, $params=null) {
 201		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 202			throw new InvalidArgumentException('account must be string representing accountId');
 203		}
 204		if (is_array($params)) {
 205			$params = $this->_filterParams($params, array('active_after','active_before','limit','offset','search'));
 206			if ($params === false) {
 207				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 208			}
 209		}
 210		return $this->get($account, 'contacts', $params);
 211	}
 212
 213	public function getContact($account, $params=array()) {
 214		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 215			throw new InvalidArgumentException('account must be string representing accountId');
 216		}
 217		if (is_string($params)) {
 218			$params = array('email' => $params);
 219		}
 220		else {
 221			$params = $this->_filterParams($params, array('email'), array('email'));
 222			if ($params === false) {
 223				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 224			}
 225		}
 226		return $this->get($account, 'contacts/' . $params['email']);
 227	}
 228
 229	/**
 230	 * @link http://context.io/docs/2.0/accounts/contacts/files
 231	 * @return ContextIOResponse
 232	 */
 233	public function listContactFiles($account, $params) {
 234		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 235			throw new InvalidArgumentException('account must be string representing accountId');
 236		}
 237		$params = $this->_filterParams($params, array('email','limit','offset','scope','group_by_revisions','include_person_info'), array('email'));
 238		if ($params === false) {
 239			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 240		}
 241		return $this->get($account, 'contacts/' . $params['email'] . '/files', $params);
 242	}
 243
 244	/**
 245	 * @link http://context.io/docs/2.0/accounts/contacts/messages
 246	 * @return ContextIOResponse
 247	 */
 248	public function listContactMessages($account, $params) {
 249		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 250			throw new InvalidArgumentException('account must be string representing accountId');
 251		}
 252		$params = $this->_filterParams($params, array('email','limit','offset','scope','folder','include_person_info'), array('email'));
 253		if ($params === false) {
 254			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 255		}
 256		return $this->get($account, 'contacts/' . $params['email'] . '/messages', $params);
 257	}
 258
 259	/**
 260	 * @link http://context.io/docs/2.0/accounts/contacts/threads
 261	 * @return ContextIOResponse
 262	 */
 263	public function listContactThreads($account, $params) {
 264		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 265			throw new InvalidArgumentException('account must be string representing accountId');
 266		}
 267		$params = $this->_filterParams($params, array('email','limit','offset','scope','folder'), array('email'));
 268		if ($params === false) {
 269			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 270		}
 271		return $this->get($account, 'contacts/' . $params['email'] . '/threads', $params);
 272	}
 273
 274	/**
 275	 * @link http://context.io/docs/2.0/accounts/files
 276	 * @param string $account accountId of the mailbox you want to query
 277	 * @param array[string]mixed $params Query parameters for the API call: indexed_after, limit
 278	 * @return ContextIOResponse
 279	 */
 280	public function listFiles($account, $params=null) {
 281		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 282			throw new InvalidArgumentException('account must be string representing accountId');
 283		}
 284		if (is_array($params)) {
 285			$params = $this->_filterParams($params, array('indexed_before', 'indexed_after','date_before','date_after','file_name','limit', 'offset', 'email', 'to','from','cc','bcc','group_by_revisions','include_person_info'));
 286			if ($params === false) {
 287				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 288			}
 289		}
 290		return $this->get($account, 'files', $params);
 291	}
 292
 293	public function getFile($account, $params) {
 294		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 295			throw new InvalidArgumentException('account must be string representing accountId');
 296		}
 297		if (is_string($params)) {
 298			$params = array('file_id' =>$params);
 299		}
 300		else {
 301			$params = $this->_filterParams($params, array('file_id'), array('file_id'));
 302			if ($params === false) {
 303				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 304			}
 305		}
 306		return $this->get($account, 'files/' . $params['file_id']);
 307	}
 308
 309	public function getFileURL($account, $params) {
 310		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 311			throw new InvalidArgumentException('account must be string representing accountId');
 312		}
 313		if (is_string($params)) {
 314			$params = array('file_id' =>$params);
 315		}
 316		else {
 317			$params = $this->_filterParams($params, array('file_id'), array('file_id'));
 318		}
 319		return $this->get($account, 'files/' . $params['file_id'] . '/content', array('as_link' => 1), array('text/uri-list'));
 320	}
 321
 322	/**
 323	 * Returns the content a given attachment. If you want to save the attachment to
 324	 * a file, set $saveAs to the destination file name. If $saveAs is left to null,
 325	 * the function will return the file data.
 326	 * on the
 327	 * @link http://context.io/docs/2.0/accounts/files/content
 328	 * @param string $account accountId of the mailbox you want to query
 329	 * @param array[string]string $params Query parameters for the API call: 'fileId'
 330	 * @param string $saveAs Path to local file where the attachment should be saved to.
 331	 * @return mixed
 332	 */
 333	public function getFileContent($account, $params, $saveAs=null) {
 334		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 335			throw new InvalidArgumentException('account must be string representing accountId');
 336		}
 337		if (is_string($params)) {
 338			$params = array('file_id' =>$params);
 339		}
 340		else {
 341			$params = $this->_filterParams($params, array('file_id'), array('file_id'));
 342			if ($params === false) {
 343				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 344			}
 345		}
 346
 347		$consumer = new ContextIOExtLib\OAuthConsumer($this->oauthKey, $this->oauthSecret);
 348		$baseUrl = $this->build_url('accounts/' . $account . '/files/' . $params['file_id'] . '/content');
 349		$req = ContextIOExtLib\OAuthRequest::from_consumer_and_token($consumer, null, "GET", $baseUrl);
 350		$sig_method = new ContextIOExtLib\OAuthSignatureMethod_HMAC_SHA1();
 351		$req->sign_request($sig_method, $consumer, null);
 352
 353		//get data using signed url
 354		if ($this->authHeaders) {
 355			$curl = curl_init($baseUrl);
 356			curl_setopt($curl, CURLOPT_HTTPHEADER, array($req->to_header()));
 357		}
 358		else {
 359			$curl = curl_init($req->to_url());
 360		}
 361
 362		if ($this->ssl) {
 363			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
 364		}
 365
 366		curl_setopt($curl, CURLOPT_USERAGENT, 'ContextIOLibrary/2.0 (PHP)');
 367
 368		if (! is_null($saveAs)) {
 369			$fp = fopen($saveAs, "w");
 370			curl_setopt($curl, CURLOPT_FILE, $fp);
 371			curl_setopt($curl, CURLOPT_HEADER, 0);
 372			curl_exec($curl);
 373			curl_close($curl);
 374			fclose($fp);
 375			return true;
 376		}
 377		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
 378		$result = curl_exec($curl);
 379		if (curl_getinfo($curl, CURLINFO_HTTP_CODE) != 200) {
 380			$response = new ContextIOResponse(
 381				curl_getinfo($curl, CURLINFO_HTTP_CODE),
 382				null,
 383				null,
 384				curl_getinfo($curl, CURLINFO_CONTENT_TYPE),
 385				$result);
 386			$this->lastResponse = $response;
 387			curl_close($curl);
 388			return false;
 389		}
 390		curl_close($curl);
 391		return $result;
 392	}
 393
 394	/**
 395	 * Given two files, this will return the list of insertions and deletions made
 396	 * from the oldest of the two files to the newest one.
 397	 * @link http://context.io/docs/2.0/accounts/files/changes
 398	 * @param string $account accountId of the mailbox you want to query
 399	 * @param array[string]string $params Query parameters for the API call: 'fileId1', 'fileId2'
 400	 * @return ContextIOResponse
 401	 */
 402	public function getFileChanges($account, $params) {
 403		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 404			throw new InvalidArgumentException('account must be string representing accountId');
 405		}
 406		$params = $this->_filterParams($params, array('file_id1', 'file_id2', 'generate'), array('file_id1','file_id2'));
 407		if ($params === false) {
 408			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 409		}
 410		$newParams = array(
 411			'file_id' => $params['file_id2']
 412		);
 413		if (! array_key_exists('generate', $params)) {
 414			$newParams['generate'] = 1;
 415		}
 416		else {
 417			$newParams['generate'] = $params['generate'];
 418		}
 419		return $this->get($account, 'files/' . $params['file_id1'] . '/changes', $newParams);
 420	}
 421
 422	/**
 423	 * Returns a list of revisions attached to other emails in the
 424	 * mailbox for one or more given files (see fileid parameter below).
 425	 * @link http://context.io/docs/2.0/accounts/files/revisions
 426	 * @param string $account accountId of the mailbox you want to query
 427	 * @param array[string]string $params Query parameters for the API call: 'fileId', 'fileName'
 428	 * @return ContextIOResponse
 429	 */
 430	public function listFileRevisions($account, $params) {
 431		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 432			throw new InvalidArgumentException('account must be string representing accountId');
 433		}
 434		if (is_string($params)) {
 435			$params = array('file_id' =>$params);
 436		}
 437		else {
 438			$params = $this->_filterParams($params, array('file_id', 'include_person_info'), array('file_id'));
 439			if ($params === false) {
 440				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 441			}
 442		}
 443		return $this->get($account, 'files/' . $params['file_id'] . '/revisions', $params);
 444	}
 445
 446	/**
 447	 * Returns a list of files that are related to the given file.
 448	 * Currently, relation between files is based on how similar their names are.
 449	 * You must specify either the fileId of fileName parameter
 450	 * @link http://context.io/docs/2.0/accounts/files/related
 451	 * @param string $account accountId of the mailbox you want to query
 452	 * @param array[string]string $params Query parameters for the API call: 'fileId', 'fileName'
 453	 * @return ContextIOResponse
 454	 */
 455	public function listFileRelated($account, $params) {
 456		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 457			throw new InvalidArgumentException('account must be string representing accountId');
 458		}
 459		if (is_string($params)) {
 460			$params = array('file_id' =>$params);
 461		}
 462		else {
 463			$params = $this->_filterParams($params, array('file_id','include_person_info'), array('file_id'));
 464			if ($params === false) {
 465				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 466			}
 467		}
 468		return $this->get($account, 'files/' . $params['file_id'] . '/related', $params);
 469	}
 470
 471	/**
 472	 * Returns message information
 473	 * @param string $account accountId of the mailbox you want to query
 474	 * @param array[string]mixed $params Query parameters for the API call: 'subject', 'limit'
 475	 * @return ContextIOResponse
 476	 */
 477	public function listMessagesBySourceAndFolder($account, $params=null) {
 478		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 479			throw new InvalidArgumentException('account must be string representing accountId');
 480		}
 481		if (is_array($params)) {
 482			$params = $this->_filterParams($params, array('label','folder','limit','offset','type','include_body','include_headers','include_flags','flag_seen'), array('label','folder'));
 483			if ($params === false) {
 484				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 485			}
 486		}
 487		$source = $params['label'];
 488		$folder = $params['folder'];
 489		unset($params['label']);
 490		unset($params['folder']);
 491		return $this->get($account, "sources/$source/folders/$folder/messages", $params);
 492	}
 493
 494	/**
 495	 * Returns message information
 496	 * @link http://context.io/docs/2.0/accounts/messages
 497	 * @param string $account accountId of the mailbox you want to query
 498	 * @param array[string]mixed $params Query parameters for the API call: 'subject', 'limit'
 499	 * @return ContextIOResponse
 500	 */
 501	public function listMessages($account, $params=null) {
 502		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 503			throw new InvalidArgumentException('account must be string representing accountId');
 504		}
 505		if (is_array($params)) {
 506			$params = $this->_filterParams($params, array('subject', 'date_before', 'date_after', 'indexed_after', 'indexed_before', 'limit', 'offset','email', 'to','from','cc','bcc','email_message_id','type','include_body','include_headers','include_flags','folder','gm_search','include_person_info'));
 507			if ($params === false) {
 508				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 509			}
 510		}
 511		return $this->get($account, 'messages', $params);
 512	}
 513
 514	public function addMessageToFolder($account, $params=array()) {
 515		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 516			throw new InvalidArgumentException('account must be string representing accountId');
 517		}
 518		$params = $this->_filterParams($params, array('dst_label','dst_folder','src_file','message_id','email_message_id','gmail_message_id'), array('dst_label','dst_folder'));
 519		if ($params === false) {
 520			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 521		}
 522		if (array_key_exists('src_file', $params)) {
 523			$params['src_file'] = realpath($params['src_file']);
 524			if (($params['src_file'] === false) || !is_readable($params['src_file'])) {
 525				throw new InvalidArgumentException("invalid source file");
 526			}
 527			$src_file = '@' . $params['src_file'];
 528			unset($params['src_file']);
 529			return $this->post($account, 'messages', $params, array('field' => 'message', 'filename' => $src_file));
 530		}
 531		elseif (array_key_exists('message_id', $params)) {
 532			return $this->post($account, 'messages/' . $params['message_id'], $params);
 533		}
 534		elseif (array_key_exists('email_message_id', $params)) {
 535			return $this->post($account, 'messages/' . urlencode($params['email_message_id']), $params);
 536		}
 537		elseif (array_key_exists('gmail_message_id', $params)) {
 538			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 539				return $this->post($account, 'messages/' . $params['gmail_message_id'], $params);
 540			}
 541			return $this->post($account, 'messages/gm-' . $params['gmail_message_id'], $params);
 542		}
 543		else {
 544			throw new InvalidArgumentException('src_file, message_id, email_message_id or gmail_message_id is a required hash key');
 545		}
 546	}
 547
 548	/**
 549	 * Returns document and contact information about a message.
 550	 * A message can be identified by the value of its Message-ID header
 551	 * @link http://context.io/docs/2.0/accounts/messages#id-get
 552	 * @param string $account accountId of the mailbox you want to query
 553	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 554	 * @return ContextIOResponse
 555	 */
 556	public function getMessage($account, $params) {
 557		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 558			throw new InvalidArgumentException('account must be string representing accountId');
 559		}
 560		if (is_string($params)) {
 561			return $this->get($account, 'messages/' . urlencode($params));
 562		}
 563		else {
 564			$params = $this->_filterParams($params, array('message_id', 'email_message_id', 'gmail_message_id', 'include_person_info', 'type','include_body','include_headers','include_flags'));
 565			if ($params === false) {
 566				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 567			}
 568			if (array_key_exists('message_id', $params)) {
 569				return $this->get($account, 'messages/' . $params['message_id'], $params);
 570			}
 571			elseif (array_key_exists('email_message_id', $params)) {
 572				return $this->get($account, 'messages/' . urlencode($params['email_message_id']), $params);
 573			}
 574			elseif (array_key_exists('gmail_message_id', $params)) {
 575				if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 576					return $this->get($account, 'messages/' . $params['gmail_message_id'], $params);
 577				}
 578				return $this->get($account, 'messages/gm-' . $params['gmail_message_id'], $params);
 579			}
 580			else {
 581				throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 582			}
 583		}
 584	}
 585
 586	/**
 587	 * Returns the message headers of a message.
 588	 * A message can be identified by the value of its Message-ID header
 589	 * @link http://context.io/docs/2.0/accounts/messages/headers
 590	 * @param string $account accountId of the mailbox you want to query
 591	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 592	 * @return ContextIOResponse
 593	 */
 594	public function getMessageHeaders($account, $params) {
 595		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 596			throw new InvalidArgumentException('account must be string representing accountId');
 597		}
 598		if (is_string($params)) {
 599			return $this->get($account, 'messages/' . urlencode($params) . '/headers');
 600		}
 601		else {
 602			$params = $this->_filterParams($params, array('message_id','email_message_id', 'gmail_message_id', 'raw'), array());
 603			if (array_key_exists('message_id', $params)) {
 604				return $this->get($account, 'messages/' . $params['message_id']. '/headers', $params);
 605			}
 606			elseif (array_key_exists('email_message_id', $params)) {
 607				return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/headers', $params);
 608			}
 609			elseif (array_key_exists('gmail_message_id', $params)) {
 610				if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 611					return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/headers', $params);
 612				}
 613				return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/headers', $params);
 614			}
 615			else {
 616				throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 617			}
 618		}
 619	}
 620
 621	/**
 622	 * Returns the message source of a message.
 623	 * A message can be identified by the value of its Message-ID header
 624	 * @link http://context.io/docs/2.0/accounts/messages/source
 625	 * @param string $account accountId of the mailbox you want to query
 626	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 627	 * @return ContextIOResponse
 628	 */
 629	public function getMessageSource($account, $params, $saveAs=null) {
 630		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 631			throw new InvalidArgumentException('account must be string representing accountId');
 632		}
 633		if (is_string($params)) {
 634			$url = 'messages/' . urlencode($params) . '/source';
 635		}
 636		elseif (array_key_exists('message_id', $params)) {
 637			$url = 'messages/' . $params['message_id']. '/source';
 638		}
 639		elseif (array_key_exists('email_message_id', $params)) {
 640			$url = 'messages/' . urlencode($params['email_message_id']) . '/source';
 641		}
 642		elseif (array_key_exists('gmail_message_id', $params)) {
 643			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 644				$url = 'messages/' . $params['gmail_message_id'] . '/source';
 645			}
 646			else {
 647				$url = 'messages/gm-' . $params['gmail_message_id'] . '/source';
 648			}
 649		}
 650		else {
 651			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 652		}
 653
 654		$consumer = new ContextIOExtLib\OAuthConsumer($this->oauthKey, $this->oauthSecret);
 655		$baseUrl = $this->build_url('accounts/' . $account . '/' . $url);
 656		$req = ContextIOExtLib\OAuthRequest::from_consumer_and_token($consumer, null, "GET", $baseUrl);
 657		$sig_method = new ContextIOExtLib\OAuthSignatureMethod_HMAC_SHA1();
 658		$req->sign_request($sig_method, $consumer, null);
 659
 660		//get data using signed url
 661		if ($this->authHeaders) {
 662			$curl = curl_init($baseUrl);
 663			curl_setopt($curl, CURLOPT_HTTPHEADER, array($req->to_header()));
 664		}
 665		else {
 666			$curl = curl_init($req->to_url());
 667		}
 668
 669		if ($this->ssl) {
 670			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
 671		}
 672
 673		curl_setopt($curl, CURLOPT_USERAGENT, 'ContextIOLibrary/2.0 (PHP)');
 674
 675		if (! is_null($saveAs)) {
 676			$fp = fopen($saveAs, "w");
 677			curl_setopt($curl, CURLOPT_FILE, $fp);
 678			curl_setopt($curl, CURLOPT_HEADER, 0);
 679			curl_exec($curl);
 680			curl_close($curl);
 681			fclose($fp);
 682			return true;
 683		}
 684		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
 685		$result = curl_exec($curl);
 686		if (curl_getinfo($curl, CURLINFO_HTTP_CODE) != 200) {
 687			$response = new ContextIOResponse(
 688				curl_getinfo($curl, CURLINFO_HTTP_CODE),
 689				null,
 690				null,
 691				curl_getinfo($curl, CURLINFO_CONTENT_TYPE),
 692				$result);
 693			$this->lastResponse = $response;
 694			curl_close($curl);
 695			return false;
 696		}
 697		curl_close($curl);
 698		return $result;
 699	}
 700
 701	/**
 702	 * Returns the message flags of a message.
 703	 * A message can be identified by the value of its Message-ID header
 704	 * @link http://context.io/docs/2.0/accounts/messages/flags
 705	 * @param string $account accountId of the mailbox you want to query
 706	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 707	 * @return ContextIOResponse
 708	 */
 709	public function getMessageFlags($account, $params) {
 710		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 711			throw new InvalidArgumentException('account must be string representing accountId');
 712		}
 713		if (is_string($params)) {
 714			return $this->get($account, 'messages/' . urlencode($params) . '/flags');
 715		}
 716		elseif (array_key_exists('message_id', $params)) {
 717			return $this->get($account, 'messages/' . $params['message_id']. '/flags');
 718		}
 719		elseif (array_key_exists('email_message_id', $params)) {
 720			return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/flags');
 721		}
 722		elseif (array_key_exists('gmail_message_id', $params)) {
 723			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 724				return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/flags');
 725			}
 726			return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/flags');
 727		}
 728		else {
 729			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 730		}
 731	}
 732
 733	/**
 734	 * Returns the folders the message is part of.
 735	 * A message can be identified by the value of its Message-ID header
 736	 * @link http://context.io/docs/2.0/accounts/messages/folders
 737	 * @param string $account accountId
 738	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 739	 * @return ContextIOResponse
 740	 */
 741	public function getMessageFolders($account, $params) {
 742		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 743			throw new InvalidArgumentException('account must be string representing accountId');
 744		}
 745		if (is_string($params)) {
 746			return $this->get($account, 'messages/' . urlencode($params) . '/folders');
 747		}
 748		elseif (array_key_exists('message_id', $params)) {
 749			return $this->get($account, 'messages/' . $params['message_id']. '/folders');
 750		}
 751		elseif (array_key_exists('email_message_id', $params)) {
 752			return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/folders');
 753		}
 754		elseif (array_key_exists('gmail_message_id', $params)) {
 755			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 756				return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/folders');
 757			}
 758			return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/folders');
 759		}
 760		else {
 761			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 762		}
 763	}
 764
 765	/**
 766	 * Sets the message folders of a message.
 767	 * A message can be identified by the value of its Message-ID header
 768	 * @param string $account accountId of the mailbox you want to query
 769	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 770	 * @return ContextIOResponse
 771	 */
 772	public function setMessageFolders($account, $params) {
 773		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 774			throw new InvalidArgumentException('account must be string representing accountId');
 775		}
 776		$params = $this->_filterParams($params, array('message_id', 'email_message_id', 'gmail_message_id', 'add','remove','folders'));
 777		if ($params === false) {
 778			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 779		}
 780		if (array_key_exists('folders', $params)) {
 781			if (! is_array($params['folders'])) {
 782				throw new InvalidArgumentException("folders must be array");
 783			}
 784			$folderStr = json_encode($params['folders']);
 785			if (array_key_exists('email_message_id', $params)) {
 786				return $this->put($account, 'messages/' . urlencode($params['email_message_id']) . '/folders', $folderStr);
 787			}
 788			elseif (array_key_exists('message_id', $params)) {
 789				return $this->put($account, 'messages/' . $params['message_id'] . '/folders', $folderStr);
 790			}
 791			elseif (array_key_exists('gmail_message_id', $params)) {
 792				if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 793					return $this->put($account, 'messages/' . $params['gmail_message_id'] . '/folders', $folderStr);
 794				}
 795				return $this->put($account, 'messages/gm-' . $params['gmail_message_id'] . '/folders', $folderStr);
 796			}
 797			else {
 798				throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 799			}
 800		}
 801		else {
 802			$addRemoveParams = array();
 803			foreach (array('add','remove') as $currentName) {
 804				if (array_key_exists($currentName, $params)) {
 805					$addRemoveParams[$currentName] = $params[$currentName];
 806				}
 807			}
 808			if (count(array_keys($addRemoveParams)) == 0) {
 809				throw new InvalidArgumentException("must specify at least one of add,remove");
 810			}
 811
 812			if (array_key_exists('email_message_id', $params)) {
 813				return $this->post($account, 'messages/' . urlencode($params['email_message_id']) . '/folders', $addRemoveParams);
 814			}
 815			elseif (array_key_exists('message_id', $params)) {
 816				return $this->post($account, 'messages/' . $params['message_id'] . '/folders', $addRemoveParams);
 817			}
 818			elseif (array_key_exists('gmail_message_id', $params)) {
 819				if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 820					return $this->post($account, 'messages/' . $params['gmail_message_id'] . '/folders', $addRemoveParams);
 821				}
 822				return $this->post($account, 'messages/gm-' . $params['gmail_message_id'] . '/folders', $addRemoveParams);
 823			}
 824			else {
 825				throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 826			}
 827		}
 828	}
 829
 830	/**
 831	 * Returns the message flags of a message.
 832	 * A message can be identified by the value of its Message-ID header
 833	 * @link http://context.io/docs/2.0/accounts/messages/flags
 834	 * @param string $account accountId of the mailbox you want to query
 835	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId'
 836	 * @return ContextIOResponse
 837	 */
 838	public function setMessageFlags($account, $params) {
 839		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 840			throw new InvalidArgumentException('account must be string representing accountId');
 841		}
 842		$params = $this->_filterParams($params, array('message_id', 'email_message_id', 'gmail_message_id', 'seen','answered','flagged','deleted','draft'));
 843		if ($params === false) {
 844			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 845		}
 846		$flagParams = array();
 847		foreach (array('seen','answered','flagged','deleted','draft') as $currentFlagName) {
 848			if (array_key_exists($currentFlagName, $params)) {
 849				if (! is_bool($params[$currentFlagName])) {
 850					throw new InvalidArgumentException("$currentFlagName must be boolean");
 851				}
 852				$flagParams[$currentFlagName] = ($params[$currentFlagName] === true) ? 1 : 0;
 853			}
 854		}
 855		if (count(array_keys($flagParams)) == 0) {
 856			throw new InvalidArgumentException("must specify at least one of seen,answered,flagged,deleted,draft");
 857		}
 858
 859		if (array_key_exists('email_message_id', $params)) {
 860			return $this->post($account, 'messages/' . urlencode($params['email_message_id']) . '/flags', $flagParams);
 861		}
 862		elseif (array_key_exists('message_id', $params)) {
 863			return $this->post($account, 'messages/' . $params['message_id'] . '/flags', $flagParams);
 864		}
 865		elseif (array_key_exists('gmail_message_id', $params)) {
 866			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 867				return $this->post($account, 'messages/' . $params['gmail_message_id'] . '/flags', $flagParams);
 868			}
 869			return $this->post($account, 'messages/gm-' . $params['gmail_message_id'] . '/flags', $flagParams);
 870		}
 871		else {
 872			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 873		}
 874	}
 875
 876	/**
 877	 * Returns the message body (excluding attachments) of a message.
 878	 * A message can be identified by the value of its Message-ID header
 879	 * or by the combination of the date sent timestamp and email address
 880	 * of the sender.
 881	 * @link http://context.io/docs/2.0/accounts/messages/body
 882	 * @param string $account accountId of the mailbox you want to query
 883	 * @param array[string]mixed $params Query parameters for the API call: 'emailMessageId', 'from', 'dateSent','type
 884	 * @return ContextIOResponse
 885	 */
 886	public function getMessageBody($account, $params) {
 887		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 888			throw new InvalidArgumentException('account must be string representing accountId');
 889		}
 890		if (is_string($params)) {
 891			return $this->get($account, 'messages/' . urlencode($params) . '/body');
 892		}
 893		$params = $this->_filterParams($params, array('message_id', 'email_message_id', 'gmail_message_id', 'type'));
 894		if ($params === false) {
 895			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 896		}
 897		if (array_key_exists('email_message_id', $params)) {
 898			return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/body', $params);
 899		}
 900		elseif (array_key_exists('message_id', $params)) {
 901			return $this->get($account, 'messages/' . $params['message_id'] . '/body', $params);
 902		}
 903		elseif (array_key_exists('gmail_message_id', $params)) {
 904			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 905				return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/body', $params);
 906			}
 907			return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/body', $params);
 908		}
 909		else {
 910			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 911		}
 912	}
 913
 914	/**
 915	 * Returns message and contact information about a given email thread.
 916	 * @link http://context.io/docs/2.0/accounts/messages/thread
 917	 * @param string $account accountId of the mailbox you want to query
 918	 * @param array[string]string $params Query parameters for the API call: 'email_message_id'
 919	 * @return ContextIOResponse
 920	 */
 921	public function getMessageThread($account, $params) {
 922		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 923			throw new InvalidArgumentException('account must be string representing accountId');
 924		}
 925		if (is_string($params)) {
 926			return $this->get($account, 'messages/' . urlencode($params) . '/thread');
 927		}
 928		$params = $this->_filterParams($params, array('message_id', 'email_message_id', 'gmail_message_id', 'include_body', 'include_headers', 'include_flags', 'type', 'include_person_info'));
 929		if ($params === false) {
 930			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 931		}
 932		if (array_key_exists('email_message_id', $params)) {
 933			return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/thread', $params);
 934		}
 935		elseif (array_key_exists('message_id', $params)) {
 936			return $this->get($account, 'messages/' . $params['message_id'] . '/thread', $params);
 937		}
 938		elseif (array_key_exists('gmail_message_id', $params)) {
 939			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 940				return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/thread', $params);
 941			}
 942			return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/thread', $params);
 943		}
 944		else {
 945			throw new InvalidArgumentException('message_id, email_message_id or gmail_message_id is a required hash key');
 946		}
 947	}
 948
 949	/**
 950	 * Returns list of threads
 951	 * @link http://context.io/docs/2.0/accounts/threads
 952	 * @param string $account accountId of the mailbox you want to query
 953	 * @param array[string]string $params Query parameters for the API call: 'gmailthreadid'
 954	 * @return ContextIOResponse
 955	 */
 956	public function listThreads($account, $params=null) {
 957		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 958			throw new InvalidArgumentException('account must be string representing accountId');
 959		}
 960		if (is_array($params)) {
 961			$params = $this->_filterParams($params, array('subject', 'indexed_after', 'indexed_before', 'active_after', 'active_before', 'started_after', 'started_before', 'limit', 'offset','email', 'to','from','cc','bcc','folder'));
 962			if ($params === false) {
 963				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 964			}
 965		}
 966		return $this->get($account, 'threads', $params);
 967	}
 968
 969	/**
 970	 * Returns message and contact information about a given email thread.
 971	 * @link http://context.io/docs/2.0/accounts/threads
 972	 * @param string $account accountId of the mailbox you want to query
 973	 * @param array[string]string $params Query parameters for the API call: 'gmailthreadid'
 974	 * @return ContextIOResponse
 975	 */
 976	public function getThread($account, $params) {
 977		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
 978			throw new InvalidArgumentException('account must be string representing accountId');
 979		}
 980		$params = $this->_filterParams($params, array('message_id', 'gmail_thread_id','gmail_message_id','email_message_id','include_body','include_headers','include_flags','type','include_person_info','limit','offset'));
 981		if ($params === false) {
 982			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
 983		}
 984		if (array_key_exists('email_message_id', $params)) {
 985			return $this->get($account, 'messages/' . urlencode($params['email_message_id']) . '/thread', $params);
 986		}
 987		elseif (array_key_exists('message_id', $params)) {
 988			return $this->get($account, 'messages/' . $params['message_id'] . '/thread', $params);
 989		}
 990		elseif (array_key_exists('gmail_message_id', $params)) {
 991			if (substr($params['gmail_message_id'],0,3) == 'gm-') {
 992				return $this->get($account, 'messages/' . $params['gmail_message_id'] . '/thread', $params);
 993			}
 994			return $this->get($account, 'messages/gm-' . $params['gmail_message_id'] . '/thread', $params);
 995		}
 996		elseif (array_key_exists('gmail_thread_id', $params)) {
 997			if (substr($params['gmail_thread_id'],0,3) == 'gm-') {
 998				return $this->get($account, 'threads/' . $params['gmail_thread_id'], $params);
 999			}
1000			return $this->get($account, 'threads/gm-' . $params['gmail_thread_id'], $params);
1001		}
1002		else {
1003			throw new InvalidArgumentException('gmail_thread_id, messageId, email_message_id or gmail_message_id are required hash keys');
1004		}
1005	}
1006
1007
1008	public function addAccount($params) {
1009		$params = $this->_filterParams($params, array('email','first_name','last_name','type','server','username','provider_consumer_key','provider_token','provider_token_secret','service_level','sync_period','password','use_ssl','port','callback_url'), array('email'));
1010		if ($params === false) {
1011			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1012		}
1013		return $this->post(null, 'accounts', $params);
1014	}
1015
1016	public function modifyAccount($account, $params) {
1017		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1018			throw new InvalidArgumentException('account must be string representing accountId');
1019		}
1020		$params = $this->_filterParams($params, array('first_name','last_name'));
1021		if ($params === false) {
1022			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1023		}
1024		return $this->post($account, '', $params);
1025	}
1026
1027	public function getAccount($account) {
1028		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1029			throw new InvalidArgumentException('account must be string representing accountId');
1030		}
1031		return $this->get($account);
1032	}
1033
1034	public function deleteAccount($account) {
1035		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1036			throw new InvalidArgumentException('account must be string representing accountId');
1037		}
1038		return $this->delete($account);
1039	}
1040
1041	public function listAccountEmailAddresses($account) {
1042		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1043			throw new InvalidArgumentException('account must be string representing accountId');
1044		}
1045		return $this->get($account, 'email_addresses');
1046	}
1047
1048	public function addEmailAddressToAccount($account, $params) {
1049		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1050			throw new InvalidArgumentException('account must be string representing accountId');
1051		}
1052		$params = $this->_filterParams($params, array('email_address'), array('email_address'));
1053		if ($params === false) {
1054			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1055		}
1056		return $this->post($account, 'email_addresses', $params);
1057	}
1058
1059	public function deleteEmailAddressFromAccount($account, $params) {
1060		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1061			throw new InvalidArgumentException('account must be string representing accountId');
1062		}
1063		if (is_string($params)) {
1064			return $this->delete($account, 'email_addresses/' . $params);
1065		}
1066		$params = $this->_filterParams($params, array('email_address'), array('email_address'));
1067		if ($params === false) {
1068			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1069		}
1070		return $this->delete($account, 'email_addresses/' . $params['email_address']);
1071	}
1072
1073	public function setPrimaryEmailAddressForAccount($account, $params) {
1074		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1075			throw new InvalidArgumentException('account must be string representing accountId');
1076		}
1077		if (is_string($params)) {
1078			return $this->post($account, 'email_addresses/' . $params, array('primary' => 1));
1079		}
1080		$params = $this->_filterParams($params, array('email_address'), array('email_address'));
1081		if ($params === false) {
1082			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1083		}
1084		return $this->post($account, 'email_addresses/' . $params['email_address'], array('primary' => 1));
1085	}
1086
1087	public function listAccounts($params=null) {
1088		if (is_array($params)) {
1089			$params = $this->_filterParams($params, array('limit','offset','email','status_ok','status'));
1090			if ($params === false) {
1091				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1092			}
1093		}
1094		return $this->get(null, 'accounts', $params);
1095	}
1096
1097	/**
1098	 * Modify the IMAP server settings of an already indexed account
1099	 * @link http://context.io/docs/2.0/accounts/sources
1100	 * @param array[string]string $params Query parameters for the API call: 'credentials', 'mailboxes'
1101	 * @return ContextIOResponse
1102	 */
1103	public function modifySource($account, $params) {
1104		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1105			throw new InvalidArgumentException('account must be string representing accountId');
1106		}
1107		$params = $this->_filterParams($params, array('provider_token', 'provider_token_secret', 'password', 'provider_consumer_key', 'label', 'mailboxes', 'sync_all_folders', 'service_level','sync_period'), array('label'));
1108		if ($params === false) {
1109			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1110		}
1111		return $this->post($account, 'sources/' . $params['label'], $params);
1112	}
1113
1114	public function resetSourceStatus($account, $params) {
1115		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1116			throw new InvalidArgumentException('account must be string representing accountId');
1117		}
1118		if (is_string($params)) {
1119			$params = array('label' => $params);
1120		}
1121		else {
1122			$params = $this->_filterParams($params, array('label'), array('label'));
1123			if ($params === false) {
1124				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1125			}
1126		}
1127		return $this->post($account, 'sources/' . $params['label'], array('status' => 1));
1128	}
1129
1130	public function listSources($account, $params=null) {
1131		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1132			throw new InvalidArgumentException('account must be string representing accountId');
1133		}
1134		if (is_array($params)) {
1135			$params = $this->_filterParams($params, array('status_ok','status'));
1136			if ($params === false) {
1137				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1138			}
1139		}
1140		return $this->get($account, 'sources', $params);
1141	}
1142
1143	public function getSource($account, $params) {
1144		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1145			throw new InvalidArgumentException('account must be string representing accountId');
1146		}
1147		if (is_string($params)) {
1148			$params = array('label' => $params);
1149		}
1150		else {
1151			$params = $this->_filterParams($params, array('label'), array('label'));
1152			if ($params === false) {
1153				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1154			}
1155		}
1156		return $this->get($account, 'sources/' . $params['label']);
1157	}
1158
1159	/**
1160	 * @link http://context.io/docs/2.0/accounts/sources
1161	 * @param array[string]string $params Query parameters for the API call: 'email', 'server', 'username', 'password', 'oauthconsumername', 'oauthtoken', 'oauthtokensecret', 'usessl', 'port'
1162	 * @return ContextIOResponse
1163	 */
1164	public function addSource($account, $params) {
1165		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1166			throw new InvalidArgumentException('account must be string representing accountId');
1167		}
1168		$params = $this->_filterParams($params, array('type','email','server','username','provider_consumer_key','provider_token','provider_token_secret','service_level','sync_period','sync_all_folders','password','use_ssl','port','callback_url'), array('server','username'));
1169		if ($params === false) {
1170			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1171		}
1172		if (! array_key_exists('type', $params)) {
1173			$params['type'] = 'imap';
1174		}
1175		return $this->post($account, 'sources/', $params);
1176	}
1177
1178	/**
1179	 * Remove the connection to an IMAP account
1180	 * @link http://context.io/docs/2.0/accounts/sources
1181	 * @return ContextIOResponse
1182	 */
1183	public function deleteSource($account, $params=array()) {
1184		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1185			throw new InvalidArgumentException('account must be string representing accountId');
1186		}
1187		if (is_string($params)) {
1188			$params = array('label' => $params);
1189		}
1190		else {
1191			$params = $this->_filterParams($params, array('label'), array('label'));
1192			if ($params === false) {
1193				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1194			}
1195		}
1196		return $this->delete($account, 'sources/' . $params['label']);
1197	}
1198
1199	public function syncSource($account, $params=array()) {
1200		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1201			throw new InvalidArgumentException('account must be string representing accountId');
1202		}
1203		$params = $this->_filterParams($params, array('label'));
1204		if ($params === false) {
1205			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1206		}
1207		if ($params == array()) {
1208			return $this->post($account, 'sync');
1209		}
1210		return $this->post($account, 'sources/' . $params['label'] . '/sync');
1211	}
1212
1213	public function getSync($account, $params=array()) {
1214		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1215			throw new InvalidArgumentException('account must be string representing accountId');
1216		}
1217		$params = $this->_filterParams($params, array('label'));
1218		if ($params === false) {
1219			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1220		}
1221		if ($params == array()) {
1222			return $this->get($account, 'sync');
1223		}
1224		return $this->get($account, 'sources/' . $params['label'] . '/sync');
1225	}
1226
1227	public function addFolderToSource($account, $params=array()) {
1228		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1229			throw new InvalidArgumentException('account must be string representing accountId');
1230		}
1231		$params = $this->_filterParams($params, array('label','folder','delim'), array('label','folder'));
1232		if ($params === false) {
1233			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1234		}
1235		if (array_key_exists('delim', $params)) {
1236			return $this->put($account, 'sources/' . $params['label'] . '/folders/' . urlencode($params['folder']), array('delim' => $params['delim']));
1237		}
1238		return $this->put($account, 'sources/' . $params['label'] . '/folders/' . urlencode($params['folder']));
1239	}
1240
1241	public function sendMessage($account, $params=array()) {
1242		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1243			throw new InvalidArgumentException('account must be string representing accountId');
1244		}
1245		$params = $this->_filterParams($params, array('label','rcpt','message','message_id', 'gmail_thread_id'), array('label'));
1246		if ($params === false) {
1247			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1248		}
1249		if (! array_key_exists('message_id', $params) && ! array_key_exists('message', $params) && ! array_key_exists('gmail_thread_id', $params)) {
1250			throw new InvalidArgumentException('gmail_thread_id, message_id or message is a required hash key');
1251		}
1252
1253		return $this->post($account, 'exits/' . $params['label'], $params);
1254	}
1255
1256	public function listSourceFolders($account, $params=array()) {
1257		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1258			throw new InvalidArgumentException('account must be string representing accountId');
1259		}
1260		if (is_string($params)) {
1261			$params = array('label' => $params);
1262		}
1263		else {
1264			$params = $this->_filterParams($params, array('label'), array('label'));
1265			if ($params === false) {
1266				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1267			}
1268		}
1269		return $this->get($account, 'sources/' . $params['label'] . '/folders');
1270	}
1271
1272	public function listWebhooks($account) {
1273		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1274			throw new InvalidArgumentException('account must be string representing accountId');
1275		}
1276		return $this->get($account, 'webhooks');
1277	}
1278
1279	public function getWebhook($account, $params) {
1280		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1281			throw new InvalidArgumentException('account must be string representing accountId');
1282		}
1283		if (is_string($params)) {
1284			$params = array('webhook_id' => $params);
1285		}
1286		else {
1287			$params = $this->_filterParams($params, array('webhook_id'), array('webhook_id'));
1288			if ($params === false) {
1289				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1290			}
1291		}
1292		return $this->get($account, 'webhooks/' . $params['webhook_id']);
1293	}
1294
1295	public function addWebhook($account, $params) {
1296		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1297			throw new InvalidArgumentException('account must be string representing accountId');
1298		}
1299		$params = $this->_filterParams($params, array('filter_to', 'filter_from', 'filter_cc', 'filter_subject', 'filter_thread', 'filter_new_important', 'filter_file_name', 'filter_file_revisions', 'sync_period', 'callback_url', 'failure_notif_url','filter_folder_added','filter_folder_removed'), array('callback_url','failure_notif_url'));
1300		if ($params === false) {
1301			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1302		}
1303		return $this->post($account, 'webhooks/', $params);
1304	}
1305
1306	public function deleteWebhook($account, $params) {
1307		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1308			throw new InvalidArgumentException('account must be string representing accountId');
1309		}
1310		if (is_string($params)) {
1311			$params = array('webhook_id' => $params);
1312		}
1313		else {
1314			$params = $this->_filterParams($params, array('webhook_id'), array('webhook_id'));
1315			if ($params === false) {
1316				throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1317			}
1318		}
1319		return $this->delete($account, 'webhooks/' . $params['webhook_id']);
1320	}
1321
1322	public function modifyWebhook($account, $params) {
1323		if (is_null($account) || ! is_string($account) || (! strpos($account, '@') === false)) {
1324			throw new InvalidArgumentException('account must be string representing accountId');
1325		}
1326		$params = $this->_filterParams($params, array('webhook_id', 'active'), array('webhook_id','active'));
1327		if ($params === false) {
1328			throw new InvalidArgumentException("params array contains invalid parameters or misses required parameters");
1329		}
1330		return $this->post($account, 'webhooks/' . $params['webhook_id'], $params);
1331	}
1332
1333	/**
1334	 * Specify whether or not API calls should be made over a secure connection.
1335	 * HTTPS is used on all calls by default.
1336	 * @param bool $sslOn Set to false to make calls over HTTP, true to use HTTPS
1337	 */
1338	public function setSSL($sslOn=true) {
1339		$this->ssl = (is_bool($sslOn)) ? $sslOn : true;
1340	}
1341
1342	/**
1343	 * Set the API version. By default, the latest official version will be used
1344	 * for all calls.
1345	 * @param string $apiVersion Context.IO API version to use
1346	 * @return boolean success
1347	 */
1348	public function setApiVersion($apiVersion) {
1349		if ($apiVersion != '2.0') {
1350			return false;
1351		}
1352		$this->apiVersion = $apiVersion;
1353		return true;
1354	}
1355
1356	/**
1357	 * Specify whether OAuth parameters should be included as URL query parameters
1358	 * or sent as HTTP Authorization headers. The default is URL query parameters.
1359	 * @param bool $authHeadersOn Set to true to use HTTP Authorization headers, false to use URL query params
1360	 */
1361	public function useAuthorizationHeaders($authHeadersOn = true) {
1362		$this->authHeaders = (is_bool($authHeadersOn)) ? $authHeadersOn : true;
1363	}
1364
1365	/**
1366	 * Returns the ContextIOResponse object for the last API call.
1367	 * @return ContextIOResponse
1368	 */
1369	public function getLastResponse() {
1370		return $this->lastResponse;
1371	}
1372
1373
1374	protected function build_baseurl() {
1375		$url = 'http';
1376		if ($this->ssl) {
1377			$url = 'https';
1378		}
1379		return "$url://" . $this->endPoint . "/" . $this->apiVersion . '/';
1380	}
1381
1382	protected function build_url($action) {
1383		return $this->build_baseurl() . $action;
1384	}
1385
1386	public function saveHeaders($yes=true) {
1387		$this->saveHeaders = $yes;
1388	}
1389
1390	protected function get($account, $action='', $parameters=null, $acceptableContentTypes=null) {
1391		if (is_array($account)) {
1392			$tmp_results = array();
1393			foreach ($account as $accnt) {
1394				$result = $this->_doCall('GET', $accnt, $action, $parameters, null, $acceptableContentTypes);
1395				if ($result === false) {
1396					return false;
1397				}
1398				$tmp_results[$accnt] = $result;
1399			}
1400			return $tmp_results;
1401		}
1402		else {
1403			return $this->_doCall('GET', $account, $action, $parameters, null, $acceptableContentTypes);
1404		}
1405	}
1406
1407	protected function put($account, $action, $parameters=null) {
1408		return $this->_doCall('PUT', $account, $action, $parameters);
1409	}
1410
1411	protected function post($account, $action='', $parameters=null, $file=null) {
1412		return $this->_doCall('POST', $account, $action, $parameters, $file);
1413	}
1414
1415	protected function delete($account, $action='', $parameters=null) {
1416		return $this->_doCall('DELETE', $account, $action, $parameters);
1417	}
1418
1419	protected function _doCall($httpMethod, $account, $action, $parameters=null, $file=null, $acceptableContentTypes=null) {
1420		$consumer = new ContextIOExtLib\OAuthConsumer($this->oauthKey, $this->oauthSecret);
1421		if (! is_null($account)) {
1422			$action = 'accounts/' . $account . '/' . $action;
1423			if (substr($action,-1) == '/') {
1424				$action = substr($action,0,-1);
1425			}
1426		}
1427		$baseUrl = $this->build_url($action);
1428		$isMultiPartPost = (! is_null($file) && array_key_exists('field', $file) && array_key_exists('filename', $file));
1429		if ($isMultiPartPost || is_string($parameters)) {
1430			$this->authHeaders = true;
1431		}
1432		$req = ContextIOExtLib\OAuthRequest::from_consumer_and_token($consumer, null, $httpMethod, $baseUrl, ($isMultiPartPost || is_string($parameters)) ? array() : $parameters);
1433		$sig_method = new ContextIOExtLib\OAuthSignatureMethod_HMAC_SHA1();
1434		$req->sign_request($sig_method, $consumer, null);
1435
1436		$httpHeadersToSet = array();
1437		//get data using signed url
1438		if ($this->authHeaders) {
1439			if ($httpMethod != 'POST') {
1440				$curl = curl_init((is_null($parameters) || is_string($parameters) || (count($parameters) == 0)) ? $baseUrl : $baseUrl. '?' . ContextIOExtLib\OAuthUtil::build_http_query($parameters));
1441			}
1442			else {
1443				$curl = curl_init($baseUrl);
1444			}
1445			$httpHeadersToSet[] = $req->to_header();
1446		}
1447		else {
1448			$curl = curl_init($req->to_url());
1449		}
1450
1451		if ($this->ssl) {
1452			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
1453		}
1454
1455		curl_setopt($curl, CURLOPT_USERAGENT, 'ContextIOLibrary/2.0 (PHP)');
1456
1457		if ($httpMethod != 'GET') {
1458			if ($httpMethod == 'POST') {
1459				curl_setopt($curl, CURLOPT_POST, true);
1460				if (! is_null($parameters)) {
1461					if (is_null($file)) {
1462						curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($parameters));
1463					}
1464					else {
1465						$parameters[$file['field']] = $file['filename'];
1466						curl_setopt($curl, CURLOPT_POSTFIELDS, $parameters);
1467					}
1468				}
1469				elseif (! is_null($file)) {
1470					curl_setopt($curl, CURLOPT_POSTFIELDS, array($file['field'] => $file['filename']));
1471				}
1472			}
1473			else {
1474				curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpMethod);
1475				if (($httpMethod == 'PUT') && is_string($parameters)) {
1476					$httpHeadersToSet[] = 'Content-Length: ' . strlen($parameters);
1477					$httpHeadersToSet[] = 'Content-Type: application/json';
1478					curl_setopt($curl, CURLOPT_POSTFIELDS, $parameters); 
1479				}
1480			}
1481		}
1482		if (count($httpHeadersToSet) > 0) {
1483			curl_setopt($curl, CURLOPT_HTTPHEADER, $httpHeadersToSet);
1484		}
1485		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1486
1487		if ($this->saveHeaders) {
1488			$this->responseHeaders = array();
1489			$this->requestHeaders = array();
1490			curl_setopt($curl, CURLOPT_HEADERFUNCTION, array($this,'_setHeader'));
1491			curl_setopt($curl, CURLINFO_HEADER_OUT, 1);
1492		}
1493		$result = curl_exec($curl);
1494
1495		$httpHeadersIn = ($this->saveHeaders) ? $this->responseHeaders : null;
1496		$httpHeadersOut = ($this->saveHeaders) ? preg_split('/(\\n|\\r){1,2}/', curl_getinfo($curl, CURLINFO_HEADER_OUT)) : null;
1497
1498		if (is_null($acceptableContentTypes)) {
1499			$response = new ContextIOResponse(
1500				curl_getinfo($curl, CURLINFO_HTTP_CODE),
1501				$httpHeadersOut,
1502				$httpHeadersIn,
1503				curl_getinfo($curl, CURLINFO_CONTENT_TYPE),
1504				$result);
1505		}
1506		else {
1507			$response = new ContextIOResponse(
1508				curl_getinfo($curl, CURLINFO_HTTP_CODE),
1509				$httpHeadersOut,
1510				$httpHeadersIn,
1511				curl_getinfo($curl, CURLINFO_CONTENT_TYPE),
1512				$result,
1513				$acceptableContentTypes);
1514		}
1515		curl_close($curl);
1516		if ($response->hasError()) {
1517			$this->lastResponse = $response;
1518			return false;
1519		}
1520		return $response;
1521	}
1522
1523	public function _setHeader($curl,$headers) {
1524		$this->responseHeaders[] = trim($headers,"\n\r");
1525		return strlen($headers);
1526	}
1527
1528	protected function _filterParams($givenParams, $validParams, $requiredParams=array()) {
1529		$filteredParams = array();
1530		foreach ($givenParams as $name => $value) {
1531			if (in_array(strtolower($name), $validParams)) {
1532				$filteredParams[strtolower($name)] = $value;
1533			}
1534			else {
1535				return false;
1536			}
1537		}
1538		foreach ($requiredParams as $name) {
1539			if (! array_key_exists(strtolower($name), $filteredParams)) {
1540				return false;
1541			}
1542		}
1543		return $filteredParams;
1544	}
1545
1546}
1547
1548
1549?>