├── .gitignore ├── README └── gcalendar.class.php /.gitignore: -------------------------------------------------------------------------------- 1 | test/ 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This PHP class provides a basic implementation of the Google Calendar API (v2.1) 2 | which you can use within your own web application. While this class doesn't implement 3 | all of the features of the Google Calendar API, it serves as a light weight alternative 4 | which can get you started and perform the most basic operations on a Google Calendar. 5 | 6 | This software is released by Montania System AB, http://www.montania.se/, and licensed 7 | under the Apache 2.0 license, see http://www.apache.org/licenses/LICENSE-2.0 8 | -------------------------------------------------------------------------------- /gcalendar.class.php: -------------------------------------------------------------------------------- 1 | 28 | * @copyright Montania System AB 29 | * @version 1.0 30 | * @license http://www.apache.org/licenses/LICENSE-2.0 31 | * @package GCalendar 32 | */ 33 | 34 | 35 | /** 36 | * This class implements the Google Calendar API version 2.1. To use this class you need to provide 37 | * your username (usually an e-mail) and password to the Google account. We're of course not saving 38 | * this information in any way and all requests are sent encrypted with HTTPS. 39 | * 40 | * @package GCalendar 41 | */ 42 | class GCalendar { 43 | 44 | private $email; 45 | private $password; 46 | private $source = "Montania-GCalendar-PHP"; 47 | private $sid; 48 | private $lsid; 49 | private $auth; 50 | private $authenticated = false; 51 | 52 | /** 53 | * Class constructor to create an instance, takes email and password as arguments 54 | * @param string $email Your google account email 55 | * @param string $password Your google account password 56 | */ 57 | function __construct($email, $password) { 58 | $this->email = $email; 59 | $this->password = $password; 60 | date_default_timezone_set("Europe/Stockholm"); 61 | 62 | DEFINE("DEFAULT_MAX_EVENTS", 25); 63 | 64 | } 65 | 66 | /** 67 | * Method to authenticate the user against Google, returns false if authentication failed. 68 | * @return bool 69 | */ 70 | function authenticate() { 71 | if ($this->authenticated == true) { 72 | return true; 73 | } else if (empty($this->email) || empty($this->password)) { 74 | return false; 75 | } 76 | 77 | $ch = $this->curlPostHandle("https://www.google.com/accounts/ClientLogin", false); 78 | 79 | curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf("Email=%s&Passwd=%s&source=%s&service=cl", $this->email, $this->password, $this->source)); 80 | 81 | $response = curl_exec($ch); 82 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 83 | 84 | curl_close($ch); 85 | 86 | switch ($http_code) { 87 | case 200: 88 | preg_match("/SID=([a-z0-9_-]+)/i", $response, $sid); 89 | preg_match("/LSID=([a-z0-9_-]+)/i", $response, $lsid); 90 | preg_match("/Auth=([a-z0-9_-]+)/i", $response, $auth); 91 | 92 | $this->sid = $sid[1]; 93 | $this->lsid = $lsid[1]; 94 | $this->auth = $auth[1]; 95 | $this->authenticated = true; 96 | 97 | return true; 98 | 99 | break; 100 | 101 | case 403: 102 | return false; 103 | break; 104 | 105 | default: 106 | return false; 107 | } 108 | } 109 | 110 | /** 111 | * Helper function to check if the user is authenticated 112 | * @return bool 113 | */ 114 | function isAuthenticated() { 115 | return $this->authenticated; 116 | } 117 | 118 | /** 119 | * Method to get an array with information about all calendars assocciated with this account. 120 | * The array contains two keys, "handle" and "title". The handle can be used with getEvents() to retrieve events. 121 | * @return bool|array Returns false on failure and array on success. 122 | */ 123 | function getAllCalendars() { 124 | if ($this->authenticated === false) { 125 | return false; 126 | } 127 | 128 | $data = $this->getJsonRequest("https://www.google.com/calendar/feeds/default/allcalendars/full?alt=jsonc", true); 129 | 130 | if (!is_object($data)) { 131 | return false; 132 | } 133 | 134 | foreach ($data->data->items as $item) { 135 | $handle = str_replace(array("https://www.google.com/calendar/feeds/", "/private/full"), "", $item->eventFeedLink); 136 | $calendars[] = array('title' => $item->title, 'handle' => $handle); 137 | } 138 | 139 | return $calendars; 140 | } 141 | 142 | /** 143 | * Method to get an array with this users own calendars. 144 | * The array contains two keys, "handle" and "title". The handle can be used with getEvents() to retrieve events. 145 | * @return bool|array Returns false on failure and array on success. 146 | */ 147 | function getOwnCalendars() { 148 | if ($this->authenticated === false) { 149 | return false; 150 | } 151 | 152 | $data = $this->getJsonRequest("https://www.google.com/calendar/feeds/default/owncalendars/full?alt=jsonc", true); 153 | 154 | if (!is_object($data)) { 155 | return false; 156 | } 157 | 158 | foreach ($data->data->items as $item) { 159 | $handle = str_replace(array("https://www.google.com/calendar/feeds/", "/private/full"), "", $item->eventFeedLink); 160 | $calendars[] = array('title' => $item->title, 'handle' => $handle); 161 | } 162 | 163 | return $calendars; 164 | } 165 | 166 | /** 167 | * Method to add a new calendar to the authenticated account 168 | * 169 | * Valid colors are: 170 | * #A32929 #B1365F #7A367A #5229A3 #29527A #2952A3 #1B887A 171 | * #28754E #0D7813 #528800 #88880E #AB8B00 #BE6D00 #B1440E 172 | * #865A5A #705770 #4E5D6C #5A6986 #4A716C #6E6E41 #8D6F47 173 | * #853104 #691426 #5C1158 #23164E #182C57 #060D5E #125A12 174 | * #2F6213 #2F6309 #5F6B02 #8C500B #8C500B #754916 #6B3304 175 | * #5B123B #42104A #113F47 #333333 #0F4B38 #856508 176 | * 177 | * @param string $title Title of the calendar 178 | * @param string $details Calendar details 179 | * @param string $timezone Which timezone the calendar is in 180 | * @param bool $hidden If the calendar should be hidden or not 181 | * @param string $color Which color should be used. See above 182 | * @param string $location Location of this calendar, geographically 183 | * @return bool|object Returns false on failure and object on success 184 | */ 185 | function createCalendar($title, $details, $timezone, $hidden, $color, $location) { 186 | if ($this->authenticated === false) { 187 | return false; 188 | } else if (empty($title) || empty($timezone) || !is_bool($hidden) || empty($color) || empty($location)) { 189 | return false; 190 | } 191 | 192 | $data = array( 193 | "data" => array( 194 | "title" => $title, 195 | "details" => $details, 196 | "timeZone" => $timezone, 197 | "hidden" => $hidden, 198 | "color" => $color, 199 | "location" => $location 200 | ) 201 | ); 202 | 203 | $headers = array('Content-type: application/json'); 204 | 205 | $ch = $this->curlPostHandle("https://www.google.com/calendar/feeds/default/owncalendars/full", true, $headers); 206 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); 207 | curl_setopt($ch, CURLOPT_HEADER, true); 208 | 209 | $response = curl_exec($ch); 210 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 211 | $response_headers = $this->http_parse_headers($response); 212 | 213 | curl_close($ch); 214 | unset($ch); 215 | 216 | if ($http_code == 302) { 217 | 218 | $url = $response_headers['Location']; 219 | 220 | $ch = $this->curlPostHandle($url, true, $headers); 221 | 222 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); 223 | 224 | $response = curl_exec($ch); 225 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 226 | 227 | if ($http_code == 201) { 228 | return json_decode($response); 229 | } else { 230 | return false; 231 | } 232 | 233 | } else if ($http_code == 201) { 234 | return json_decode($response); 235 | } else { 236 | return false; 237 | } 238 | } 239 | 240 | /** 241 | * Function to delete a calendar 242 | * @param string $handle E-mail or handle to identify the calendar 243 | * @return bool Whether or not the calendar was deleted successfully 244 | */ 245 | public function deleteCalendar($handle) { 246 | 247 | $url = "https://www.google.com/calendar/feeds/default/owncalendars/full/$handle"; 248 | $ch = $this->curlDeleteHandle($url, true, array()); 249 | 250 | $response = curl_exec($ch); 251 | 252 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 253 | $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); 254 | 255 | if($http_code==200) { 256 | return true; 257 | } else { 258 | return false; 259 | } 260 | } 261 | 262 | 263 | /** 264 | * Method to retrieve events from a specific calendar. 265 | * @param string $handle E-mail or handle to identify the calendar 266 | * @param integer $max Max amount of events to get. (optional, default = 25) 267 | * @param string $from A date string where the selection should start (optional, default all events) 268 | * @param string $to A date string where the selection should end (optional, default all events) 269 | * @return bool|object Returns false on failure and object on success. 270 | */ 271 | function getEvents($handle, $max = DEFAULT_MAX_EVENTS, $from = null, $to = null) { 272 | if ($this->authenticated === false) { 273 | return false; 274 | } else if (empty($handle)) { 275 | return false; 276 | } 277 | if (empty($handle)) { 278 | $handle = "default"; 279 | } 280 | if (!is_numeric($max)) { 281 | $max = DEFAULT_MAX_EVENTS; 282 | } 283 | 284 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full?alt=jsonc&max-results=%s", $handle, $max); 285 | 286 | if ($from != null) { 287 | $from = urlencode(date("c", strtotime($from))); 288 | $url .= "&start-min=" . $from; 289 | } 290 | if ($to != null) { 291 | $to = urlencode(date("c", strtotime($to))); 292 | $url .= "&start-max=" . $to; 293 | } 294 | 295 | $ch = $this->curlGetHandle($url, true); 296 | 297 | $response = curl_exec($ch); 298 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 299 | 300 | if ($http_code == "200") { 301 | return json_decode($response); 302 | } else { 303 | return false; 304 | } 305 | } 306 | 307 | /** 308 | * Method to check if an event has been updated. 309 | * @param string $handle E-mail or handle to identify the calendar 310 | * @param string $id The ID sent by Google Calendar 311 | * @param string $etag The ETag property sent by Google Calendar 312 | * @return bool|object Returns false on failure, true if event is up to date and object if event has been changed 313 | */ 314 | function getEvent($handle, $id, $etag) { 315 | if ($this->authenticated === false) { 316 | return false; 317 | } else if (empty($id) || empty($etag)) { 318 | return false; 319 | } 320 | if (empty($handle)) { 321 | $handle = "default"; 322 | } 323 | if (substr($etag, 0, 1) != '"') { 324 | $etag = '"' . $etag; 325 | } 326 | if (substr($etag, -1, 1) != '"') { 327 | $etag .= '"'; 328 | } 329 | 330 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full/%s?alt=jsonc", $handle, $id); 331 | $ch = $this->curlGetHandle($url, true, array('If-None-Match: ' . $etag)); 332 | 333 | $response = curl_exec($ch); 334 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 335 | 336 | if ($http_code == 200) { 337 | return json_decode($response); 338 | } else if ($http_code == 304 || $http_code == 412) { 339 | return true; 340 | } else { 341 | return false; 342 | } 343 | } 344 | 345 | /** 346 | * Get an event by its entryID 347 | * @param string $handle E-mail or handle to identify the calendar 348 | * @return bool|object Returns false on failure and object on success 349 | */ 350 | function getEventByID($handle, $event_id) { 351 | if ($this->authenticated === false) { 352 | return false; 353 | } else if (empty($handle)) { 354 | return false; 355 | } 356 | // GET https://www.google.com/calendar/feeds/default/private/full/entryID 357 | $url = "https://www.google.com/calendar/feeds/$handle/private/full/$event_id?alt=jsonc"; 358 | 359 | $ch = $this->curlGetHandle($url, true); 360 | 361 | $response = curl_exec($ch); 362 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 363 | 364 | if ($http_code == "200") { 365 | $event = json_decode($response); 366 | if (!empty($event)) { 367 | return $event; 368 | } else { 369 | return array(); 370 | } 371 | } else { 372 | return false; 373 | } 374 | } 375 | 376 | 377 | /** 378 | * Method to search for an event. 379 | * @param string $handle E-mail or handle to identify the calendar 380 | * @param string $query The search query to perform 381 | * @param integer $max Max amount of events to get. (optional, default = 25) 382 | * @return bool|object Returns false on failure and object on success 383 | */ 384 | function findEvent($handle, $query, $max = DEFAULT_MAX_EVENTS) { 385 | if ($this->authenticated === false) { 386 | return false; 387 | } else if (empty($query)) { 388 | return false; 389 | } 390 | 391 | if (empty($handle)) { 392 | $handle = "default"; 393 | } 394 | if (!is_numeric($max)) { 395 | $max = DEFAULT_MAX_EVENTS; 396 | } 397 | 398 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full?q=%s&alt=jsonc&max-results=%s", $handle, urlencode($query), $max); 399 | $ch = $this->curlGetHandle($url, true); 400 | 401 | $response = curl_exec($ch); 402 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 403 | 404 | if ($http_code == 200) { 405 | return json_decode($response); 406 | } else { 407 | return false; 408 | } 409 | } 410 | 411 | /** 412 | * Method to create an event in a specific calendar. 413 | * @param string $handle E-mail or handle to identify the calendar 414 | * @param bool $quick If quick is set to true, only the details argument is needed. 415 | * @param string $details Details of the event 416 | * @param string $title Title of the event (optional in quick mode) 417 | * @param string $transparency Transparency (optional in quick mode) 418 | * @param string $status Status of the event (optional in quick mode) 419 | * @param string $location Location of the event (optional in quick mode) 420 | * @param string $start Time when the event starts (optional in quick mode) 421 | * @param string $end Time when the event ends (optional in quick mode) 422 | * @return bool|object Returns false on failure and object on success 423 | */ 424 | function createEvent($handle, $quick = false, $details, $title = null, $transparency = null, $status = null, $location = null, $start = null, $end = null) { 425 | if ($this->authenticated === false) { 426 | return false; 427 | } else if ($quick === false && (empty($title) || empty($transparency) || empty($status) || empty($location) || empty($start) || empty($end))) { 428 | return false; 429 | } else if ($quick === true && empty($details)) { 430 | return false; 431 | } 432 | 433 | if (empty($handle)) { 434 | $handle = "default"; 435 | } 436 | 437 | if ($quick === true) { 438 | $data = array("data" => array( 439 | "details" => $details, 440 | "quickAdd" => true 441 | ) 442 | ); 443 | $data = json_encode($data); 444 | } else { 445 | $data = sprintf('{ 446 | "data": { 447 | "title": "%s", 448 | "details": "%s", 449 | "transparency": "%s", 450 | "status": "%s", 451 | "location": "%s", 452 | "when": [ 453 | { 454 | "start": "%s", 455 | "end": "%s" 456 | } 457 | ] 458 | } 459 | }', $title, $details, $transparency, $status, $location, date("c", strtotime($start)), date("c", strtotime($end))); 460 | 461 | } 462 | 463 | $headers = array('Content-Type: application/json'); 464 | 465 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full", $handle); 466 | $ch = $this->curlPostHandle($url, true, $headers); 467 | 468 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 469 | curl_setopt($ch, CURLOPT_HEADER, true); 470 | 471 | $response = curl_exec($ch); 472 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 473 | $response_headers = $this->http_parse_headers($response); 474 | 475 | curl_close($ch); 476 | unset($ch); 477 | 478 | if ($http_code == 302) { 479 | 480 | $url = $response_headers['Location']; 481 | 482 | $ch = $this->curlPostHandle($url, true, $headers); 483 | 484 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 485 | 486 | $response = curl_exec($ch); 487 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 488 | 489 | if ($http_code == 201) { 490 | return json_decode($response); 491 | } else { 492 | return false; 493 | } 494 | 495 | } else if ($http_code == 201) { 496 | return json_decode($response); 497 | } else { 498 | return false; 499 | } 500 | } 501 | 502 | /** 503 | * Method to remove an event from the calendar. If $etag is submitted it won't delete your event if it has been updated since you last retreived it 504 | * @param string $handle E-mail or handle to identify the calendar 505 | * @param string $id The id of the event 506 | * @param string $etag The e-tag of the event (optional) 507 | * @return bool Returns false on failure and true on success 508 | */ 509 | function deleteEvent($handle, $id, $etag = null) { 510 | if ($this->authenticated === false) { 511 | return false; 512 | } else if (empty($handle) || empty($id)) { 513 | return false; 514 | } 515 | 516 | if (empty($handle)) { 517 | $handle = "default"; 518 | } 519 | 520 | if (!empty($etag)) { 521 | 522 | if (substr($etag, 0, 1) != '"') { 523 | $etag = '"' . $etag; 524 | } 525 | if (substr($etag, -1, 1) != '"') { 526 | $etag .= '"'; 527 | } 528 | 529 | $headers = array('If-Match: ' . $etag); 530 | } else { 531 | $headers = array('If-Match: *'); 532 | } 533 | 534 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full/%s", $handle, $id); 535 | $ch = $this->curlDeleteHandle($url, true, $headers); 536 | 537 | curl_exec($ch); 538 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 539 | 540 | return ($http_code == 200); 541 | } 542 | 543 | /** 544 | * Method to update an event in the calendar. If $etag is submitted it won't update your event if it has been updated since you last retreived it 545 | * @param string $handle E-mail or handle to identify the calendar 546 | * @param string $id The id of the event 547 | * @param string $etag The e-tag of the event (optional) 548 | * @param string $json The complete json code from the event that you've retrieved with the changes that you want 549 | * @return bool|object Returns false on failure and object on success 550 | */ 551 | function updateEvent($handle, $id, $etag, $json) { 552 | if ($this->authenticated === false) { 553 | return false; 554 | } else if (empty($handle) || empty($id) || empty($json)) { 555 | return false; 556 | } else if (!is_object(json_decode($json))) { 557 | return false; 558 | } else { 559 | $json = json_encode(json_decode($json)); 560 | } 561 | 562 | if (empty($handle)) { 563 | $handle = "default"; 564 | } 565 | 566 | $headers = array('Content-type: application/json'); 567 | 568 | 569 | if (!empty($etag)) { 570 | 571 | if (substr($etag, 0, 1) != '"') { 572 | $etag = '"' . $etag; 573 | } 574 | if (substr($etag, -1, 1) != '"') { 575 | $etag .= '"'; 576 | } 577 | 578 | $headers[] = 'If-Match: ' . $etag; 579 | } else { 580 | $headers[] = 'If-Match: *'; 581 | } 582 | 583 | $url = sprintf("https://www.google.com/calendar/feeds/%s/private/full/%s", $handle, $id); 584 | $ch = $this->curlPutHandle($url, true, $headers); 585 | 586 | curl_setopt($ch, CURLOPT_POSTFIELDS, $json); 587 | curl_setopt($ch, CURLOPT_HEADER, true); 588 | 589 | $response = curl_exec($ch); 590 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 591 | $response_headers = $this->http_parse_headers($response); 592 | 593 | curl_close($ch); 594 | unset($ch); 595 | 596 | if ($http_code == 302) { 597 | 598 | $url = $response_headers['Location']; 599 | 600 | $ch = $this->curlPutHandle($url, true, $headers); 601 | 602 | curl_setopt($ch, CURLOPT_POSTFIELDS, $json); 603 | 604 | $response = curl_exec($ch); 605 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 606 | 607 | if ($http_code == 200) { 608 | return json_decode($response); 609 | } else { 610 | return false; 611 | } 612 | 613 | } else if ($http_code == 200) { 614 | return json_decode($response); 615 | } else { 616 | return false; 617 | } 618 | } 619 | 620 | /** 621 | * Private helper function to send a HTTP GET request and return the json decoded data 622 | * @param string $url 623 | * @param bool $authenticated If the request should contain authentication information 624 | * @return bool|object Returns false on failure and object on success. 625 | */ 626 | private function getJsonRequest($url, $authenticated = false) { 627 | if (empty($url)) { 628 | return false; 629 | } 630 | 631 | $ch = $this->curlGetHandle($url, $authenticated); 632 | 633 | if ($ch === false) { 634 | return false; 635 | } 636 | 637 | $response = curl_exec($ch); 638 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 639 | curl_close($ch); 640 | 641 | if ($http_code == 200) { 642 | return json_decode($response); 643 | } else { 644 | return false; 645 | } 646 | } 647 | 648 | /** 649 | * Private helper function to get a cURL handle with the correct options, authentication included. The user has to be successfully authenticated with authenticate() first 650 | * @param string $url The URL where the http request should go 651 | * @param bool $authenticated If the request should contain authentication information 652 | * @param array $headers An array of headers to be sent with the request 653 | * @return bool|curl handle Returns false on failure and a curl handle on success 654 | */ 655 | private function curlGetHandle($url, $authenticated = false, $headers = array()) { 656 | if ($authenticated === true && $this->authenticated === false) { 657 | return false; 658 | } else if (empty($url)) { 659 | return false; 660 | } 661 | 662 | $headers[] = 'GData-Version: 2.1'; 663 | 664 | if ($authenticated === true) { 665 | $headers[] = 'Authorization: GoogleLogin auth='. $this->auth; 666 | } 667 | 668 | $ch = curl_init(); 669 | curl_setopt($ch, CURLOPT_URL, $url); 670 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 671 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 672 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 673 | 674 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 675 | 676 | return $ch; 677 | } 678 | 679 | /** 680 | * Private helper function to get a cURL handle for POST actions with the correct options. The user has to be successfully authenticated with authenticate() first. 681 | * @param string $url The URL where the http request should go 682 | * @param bool $authenticated If the request should contain authentication information 683 | * @param array $headers An array of headers to be sent with the request 684 | * @return bool|curl handle Returns false on failure and a curl handle on success 685 | */ 686 | private function curlPostHandle($url, $authenticated = false, $headers = array()) { 687 | if ($authenticated === true && $this->authenticated === false) { 688 | return false; 689 | } else if (empty($url)) { 690 | return false; 691 | } 692 | 693 | $headers[] = 'GData-Version: 2.1'; 694 | 695 | if ($authenticated === true) { 696 | $headers[] = 'Authorization: GoogleLogin auth='. $this->auth; 697 | } 698 | 699 | $ch = curl_init(); 700 | curl_setopt($ch, CURLOPT_URL, $url); 701 | curl_setopt($ch, CURLOPT_POST, true); 702 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 703 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 704 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); 705 | 706 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 707 | 708 | return $ch; 709 | } 710 | 711 | 712 | /** 713 | * Internal method to create custom method cURL handles 714 | * @param string $url The URL where the HTTP request should go 715 | * @param string $method Can be any of DELETE, PUT, etc. 716 | * @param bool $authenticated If the request should contain authentication information (optional, default = false) 717 | * @param array $headers An array of headers to be sent with the request (optional) 718 | * @param bool $return If cURL should return data instead of printing it (optional, default = true) 719 | * @param bool $follow If cURL should follow redirects (optional, default = true) 720 | * @return bool|curl handle Returns false in failure and a curl handle on success 721 | */ 722 | private function curlCustomHandle($url, $method, $authenticated = false, $headers = array(), $return = true, $follow = true) { 723 | if ($authenticated === true && $this->authenticated === false) { 724 | return false; 725 | } else if (empty($url) || empty($method)) { 726 | return false; 727 | } 728 | 729 | $headers[] = 'GData-Version: 2.1'; 730 | 731 | if ($authenticated === true) { 732 | $headers[] = 'Authorization: GoogleLogin auth='. $this->auth; 733 | } 734 | 735 | $ch = curl_init(); 736 | curl_setopt($ch, CURLOPT_URL, $url); 737 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 738 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, $return); 739 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 740 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); 741 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $follow); 742 | 743 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 744 | 745 | return $ch; 746 | } 747 | 748 | /** 749 | * Private helper function to get a cURL handle for DELETE actions with the correct options. The user has to be successfully authenticated with authenticate() first. 750 | * @param string $url The URL where the http request should go 751 | * @param bool $authenticated If the request should contain authentication information 752 | * @param array $headers An array of headers to be sent with the request 753 | * @return bool|curl handle Returns false on failure and a curl handle on success 754 | */ 755 | private function curlDeleteHandle($url, $authenticated = false, $headers = array()) { 756 | return $this->curlCustomHandle($url, "DELETE", $authenticated, $headers); 757 | } 758 | 759 | /** 760 | * Private helper function to get a cURL handle for PUT actions with the correct options. The user has to be successfully authenticated with authenticate() first. 761 | * @param string $url The URL where the http request should go 762 | * @param bool $authenticated If the request should contain authentication information 763 | * @param array $headers An array of headers to be sent with the request 764 | * @return bool|curl handle Returns false on failure and a curl handle on success 765 | */ 766 | private function curlPutHandle($url, $authenticated = false, $headers = array()) { 767 | return $this->curlCustomHandle($url, "PUT", $authenticated, $headers, true, false); 768 | } 769 | 770 | /** 771 | * Adds user(s) to the Access Control List 772 | * @param string $handle E-mail or handle to identify the calendar 773 | * @param string $scope A person or set of people ( e-mail address / domain name / null ) 774 | * @param string $scope_type The type of scope ( user / domain / default ) 775 | * @param string $role The access level ( root / owner / editor / freebusy / read / none ) 776 | */ 777 | function addUserToACL($handle = "default", $role = "read", $scope = null, $scopeType = "default") { 778 | // POST /calendar/feeds/liz@gmail.com/acl/full 779 | $url = sprintf("https://www.google.com/calendar/feeds/%s/acl/full/", $handle); 780 | $data = array( 781 | 'data' => array( 782 | 'scopeType' => $scopeType, 783 | 'role' => $role 784 | ) 785 | ); 786 | if (!empty($scope)) { 787 | $data['data']['scope'] = $scope; 788 | } 789 | $json = json_encode($data); 790 | 791 | $headers = array('Content-type: application/json'); 792 | 793 | $ch = $this->curlPostHandle($url, true, $headers); 794 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 795 | curl_setopt($ch, CURLOPT_HEADER, true); 796 | 797 | $response = curl_exec($ch); 798 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 799 | $response_headers = $this->http_parse_headers($response); 800 | 801 | curl_close($ch); 802 | unset($ch); 803 | 804 | if ($http_code == 302) { 805 | 806 | $url = $response_headers['Location']; 807 | 808 | $ch = $this->curlPostHandle($url, true, $headers); 809 | 810 | curl_setopt($ch, CURLOPT_POSTFIELDS, ($json)); 811 | 812 | $response = curl_exec($ch); 813 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 814 | 815 | if ($http_code == 201) { 816 | return json_decode($response); 817 | } else { 818 | return false; 819 | } 820 | 821 | } else if ($http_code == 201) { 822 | return json_decode($response); 823 | } else { 824 | return false; 825 | } 826 | } 827 | 828 | /** 829 | * Creates the http_parse_headers function if pecl_http is not installed 830 | */ 831 | function http_parse_headers($header) { 832 | if(!function_exists('http_parse_headers')) { 833 | $retVal = array(); 834 | $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header)); 835 | foreach( $fields as $field ) { 836 | if( preg_match('/([^:]+): (.+)/m', $field, $match) ) { 837 | $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1]))); 838 | if( isset($retVal[$match[1]]) ) { 839 | $retVal[$match[1]] = array($retVal[$match[1]], $match[2]); 840 | } else { 841 | $retVal[$match[1]] = trim($match[2]); 842 | } 843 | } 844 | } 845 | return $retVal; 846 | } else { 847 | return http_parse_headers($header); 848 | } 849 | } 850 | } 851 | --------------------------------------------------------------------------------