GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#34)
by
unknown
02:32
created

carddav2fb.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * CardDAV to FritzBox! XML (automatic upload)
4
 * inspired by http://www.wehavemorefun.de/fritzbox/Hochladen_eines_MySQL-Telefonbuchs
5
 * 
6
 * Requirements: 
7
 *   php5, php5-curl, php5-ftp
8
 * 
9
 * used libraries: 
10
 *  *  vCard-parser <https://github.com/nuovo/vCard-parser> (LICNECE: unknown)
11
 *  *  CardDAV-PHP <https://github.com/graviox/CardDAV-PHP>(LICENCE: AGPLv3)
12
 *  *  fritzbox_api_php <https://github.com/carlos22/fritzbox_api_php> (LICENCE: CC-by-SA 3.0)
13
 * 
14
 * LICENCE (of this file): MIT
15
 * 
16
 * Autors: Karl Glatz (original author)
17
 *         Martin Rost
18
 *         Jens Maus <[email protected]>
19
 *         Johannes Freiburger
20
 *
21
 */
22
error_reporting(E_ALL);
23
setlocale(LC_ALL, 'de_DE.UTF8');
24
25
// Version identifier for CardDAV2FB
26
$carddav2fb_version = '1.11 (2016-05-12)';
27
28
// check for the minimum php version
29
$php_min_version = '5.3.6';
30
if(version_compare(PHP_VERSION, $php_min_version) < 0)
31
{
32
  print 'ERROR: PHP version ' . $php_min_version . ' is required. Found version: ' . PHP_VERSION . PHP_EOL;
33
  exit(1);
34
}
35
36
require_once('lib/CardDAV-PHP/carddav.php');
37
require_once('lib/vCard-parser/vCard.php');
38
require_once('lib/fritzbox_api_php/fritzbox_api.class.php');
39
40
if($argc == 2)
41
  $config_file_name = $argv[1];
42
else
43
  $config_file_name = __DIR__ . '/config.php';
44
45
// default/fallback config options
46
$config['tmp_dir'] = sys_get_temp_dir();
47
$config['fritzbox_ip'] = 'fritz.box';
48
$config['fritzbox_ip_ftp'] = $config['fritzbox_ip'];
49
$config['fritzbox_force_local_login'] = false;
50
$config['phonebook_number'] = '0';
51
$config['phonebook_name'] = 'Telefonbuch';
52
$config['usb_disk'] = '';
53
$config['fritzbox_path'] = 'file:///var/media/ftp/';
54
$config['fullname_format'] = 0; // see config.example.php for options
55
$config['prefix'] = false;
56
$config['suffix'] = false;
57
$config['addnames'] = false;
58
$config['orgname'] = false;
59
$config['build_photos'] = true;
60
$config['quickdial_keyword'] = 'Quickdial:';
61
62
if(is_file($config_file_name))
63
  require($config_file_name);
64
else
65
{
66
  print 'ERROR: No ' . $config_file_name . ' found, please take a look at config.example.php and create a ' . $config_file_name . ' file!' . PHP_EOL;
67
  exit(1);
68
}
69
70
// ---------------------------------------------
71
// MAIN
72
print "carddav2fb.php " . $carddav2fb_version . " - CardDAV to FRITZ!Box phonebook conversion tool" . PHP_EOL;
73
print "Copyright (c) 2012-2016 Karl Glatz, Martin Rost, Jens Maus, Johannes Freiburger" . PHP_EOL . PHP_EOL;
74
75
$client = new CardDAV2FB($config);
76
77
// read vcards from webdav
78
print 'Retrieving VCards from all CardDAV server(s):' . PHP_EOL;
79
$client->get_carddav_entries();
80
print 'Done.' . PHP_EOL;
81
82
flush(); // in case this script runs by php-cgi
83
84
// transform them to a fritzbox compatible xml file
85
print 'Converting VCards to FritzBox XML format:' . PHP_EOL;
86
$client->build_fb_xml();
87
print 'Done.' . PHP_EOL;
88
89
flush(); // in case this script runs by php-cgi
90
91
// upload the XML-file to the FRITZ!Box (CAUTION: this will overwrite all current entries in the phone book!!)
92
print 'Upload data to FRITZ!Box @ ' . $config['fritzbox_ip'] . PHP_EOL;
93
$client->upload_to_fb();
94
print 'Done.' . PHP_EOL;
95
96
flush(); // in case this script runs by php-cgi
97
98
// ---------------------------------------------
99
// Class definition
100
class CardDAV2FB
101
{
102
  protected $entries = array();
103
  protected $fbxml = "";
104
  protected $config = null;
105
  protected $tmpdir = null;
106
107
  public function __construct($config)
108
  {
109
    $this->config = $config;
110
111
    // create a temp directory where we store photos
112
    $this->tmpdir = $this->mktemp($this->config['tmp_dir']);
113
  }
114
115
  public function __destruct()
116
  {
117
    // remote temp directory
118
    $this->rmtemp($this->tmpdir);
119
  }
120
121
  // Source: https://php.net/manual/de/function.tempnam.php#61436
122
  public function mktemp($dir, $prefix = '', $mode = 0700)
123
  {
124
    if(substr($dir, -1) != '/')
125
      $dir .= '/';
126
127
    do
128
    {
129
      $path = $dir . $prefix . mt_rand(0, 9999999);
130
    }
131
    while(!mkdir($path, $mode));
132
133
    return $path;
134
  }
135
136
  public function rmtemp($dir)
137
  {
138
    if(is_dir($dir))
139
    {
140
      $objects = scandir($dir);
141
      foreach($objects as $object)
142
      {
143
        if($object != "." && $object != "..")
144
        {
145
          if(filetype($dir . "/" . $object) == "dir")
146
            rrmdir($dir . "/" . $object); else unlink($dir . "/" . $object);
147
        }
148
      }
149
      reset($objects);
150
      rmdir($dir);
151
    }
152
  }
153
154
  public function is_base64($str)
155
  {
156
    try
157
    {
158
      // Check if there are valid base64 characters
159
      if(!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $str))
160
        return false;
161
162
      // Decode the string in strict mode and check the results
163
      $decoded = base64_decode($str, true);
164
      if($decoded === false)
165
        return false;
166
167
      // Encode the string again
168
      if(base64_encode($decoded) === $str)
169
        return true;
170
      else
171
        return false;
172
    }
173
    catch(Exception $e)
174
    {
175
      // If exception is caught, then it is not a base64 encoded string
176
      return false;
177
    }
178
  }
179
180
  public function base64_to_jpeg($inputfile, $outputfile)
181
  {
182
    // read data (binary)
183
    $ifp = fopen($inputfile, "rb");
184
    $imageData = fread($ifp, filesize($inputfile));
185
    fclose($ifp);
186
187
    // encode & write data (binary)
188
    $ifp = fopen($outputfile, "wb");
189
    fwrite($ifp, base64_decode($imageData));
190
    fclose($ifp);
191
192
    // return output filename
193
    return($outputfile);
194
  }
195
196
  public function get_carddav_entries()
197
  {
198
    $entries = array();
199
    $snum = 0;
200
201
    if(is_array($this->config['carddav']))
202
    {
203
      foreach($this->config['carddav'] as $conf)
204
      {
205
        print " [" . $snum . "]: " . $conf['url'] . " ";
206
        $carddav = new CardDavPHP\CardDavBackend($conf['url']);
207
        $carddav->setAuth($conf['user'], $conf['pw']);
208
209
        // set the vcard extension in case the user
210
        // defined it in the config
211
        if(isset($conf['extension']))
212
          $carddav->setVcardExtension($conf['extension']);
213
214
        // retrieve data from the CardDAV server now
215
        $xmldata = $carddav->get();
216
217
        // identify if we received UTF-8 encoded data from the
218
        // CardDAV server and if not reencode it since the FRITZ!Box
219
        // requires UTF-8 encoded data
220
        if(iconv('utf-8', 'utf-8//IGNORE', $xmldata) != $xmldata)
221
          $xmldata = utf8_encode($xmldata);
222
223
        // read raw_vcard data from xml response
224
        $raw_vcards = array();
225
        $xmlvcard = new SimpleXMLElement($xmldata);
226
227
        foreach($xmlvcard->element as $vcard_element)
228
        {
229
          $id = $vcard_element->id->__toString();
230
          $value = (string)$vcard_element->vcard->__toString();
231
          $raw_vcards[$id] = $value;
232
        }
233
234
        print " " . count($raw_vcards) . " VCards retrieved." . PHP_EOL;
235
236
        // parse raw_vcards
237
        $quick_dial_arr = array();
238
        foreach($raw_vcards as $v)
239
        {
240
          $vcard_obj = new vCard(false, $v);
241
          $name_arr = null;
242
          if(isset($vcard_obj->n[0]))
243
            $name_arr = $vcard_obj->n[0];
244
          $org_arr = null;
245
          if(isset($vcard_obj->org[0]))
246
            $org_arr = $vcard_obj->org[0];
247
          $addnames = '';
248
          $prefix = '';
249
          $suffix = '';
250
          $orgname = '';
251
          $formattedname = '';
252
253
          // Build name Parts if existing ans switch to true in config
254
          if(isset($name_arr['prefixes']) and $this->config['prefix'])
255
            $prefix = trim($name_arr['prefixes']);
256
257
          if(isset($name_arr['suffixes']) and $this->config['suffix'])
258
            $suffix = trim($name_arr['suffixes']);
259
260
          if(isset($name_arr['additionalnames']) and $this->config['addnames'])
261
            $addnames = trim($name_arr['additionalnames']);
262
263
          if(isset($org_arr['name']) and $this->config['orgname'])
264
            $orgname = trim($org_arr['name']);
265
266
          if (isset($vcard_obj->fn[0]))
0 ignored issues
show
The property fn does not exist on object<vCard>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267
            $formattedname = $vcard_obj->fn[0];
0 ignored issues
show
The property fn does not exist on object<vCard>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
268
269
          $firstname = trim($name_arr['firstname']);
270
          $lastname = trim($name_arr['lastname']);
271
272
          // the following section implemented different ways of constructing the
273
          // final phonebook name entry depending on user preferred settings
274
          // selectable in the config file. Possible options are:
275
          //
276
          // $this->config['fullname_format']:
277
          //
278
          // 0: "Prefix Lastname, Firstname AdditionalNames Suffix (orgname)"
279
          // 1: "Prefix Firstname Lastname AdditionalNames Suffix (orgname)"
280
          // 2: "Prefix Firstname AdditionalNames Lastname Suffix (orgname)"
281
          //
282
          $name = '';
283
          $format = $this->config['fullname_format'];
284
285
          // Prefix
286
          if(!empty($prefix))
287
            $name .= $prefix;
288
289 View Code Duplication
          if($format == 0)
290
          {
291
            // Lastname
292
            if(!empty($name) and !empty($lastname))
293
              $name .= ' ' . $lastname;
294
            else
295
              $name .= $lastname;
296
          }
297
          else
298
          {
299
            // Firstname
300
            if(!empty($name) and !empty($firstname))
301
              $name .= ' ' . $firstname;
302
            else
303
              $name .= $firstname;
304
          }
305
306 View Code Duplication
          if($format == 2)
307
          {
308
            // AdditionalNames
309
            if(!empty($name) and !empty($addnames))
310
              $name .= ' ' . $addnames;
311
            else
312
              $name .= $addnames;
313
          }
314
315 View Code Duplication
          if($format == 0)
316
          {
317
            // Firstname
318
            if(!empty($name) and !empty($firstname))
319
              $name .= ', ' . $firstname;
320
            else
321
              $name .= $firstname;
322
          }
323
          else
324
          {
325
            // Lastname
326
            if(!empty($name) and !empty($lastname))
327
              $name .= ' ' . $lastname;
328
            else
329
              $name .= $lastname;
330
          }
331
332 View Code Duplication
          if($format != 2)
333
          {
334
            // AdditionalNames
335
            if(!empty($name) and !empty($addnames))
336
              $name .= ' ' . $addnames;
337
            else
338
              $name .= $addnames;
339
          }
340
341
          // Suffix
342
          if(!empty($name) and !empty($suffix))
343
            $name .= ' ' . $suffix;
344
          else
345
            $name .= $suffix;
346
347
          // OrgName
348
          if(!empty($name) and !empty($orgname))
349
            $name .= ' (' . $orgname . ')';
350
          else
351
            $name .= $orgname;
352
353
          // make sure to trim whitespaces and double spaces
354
          $name = trim(str_replace('  ', ' ', $name));
355
356
          // perform a fallback to formatted name, if we don't have any name and formatted name is available
357
          if(empty($name) and !empty($formattedname))
358
            $name = $formattedname;
359
360
          if(empty($name))
361
          {
362
            print '  WARNING: No fullname, lastname, orgname or formatted name found!' . PHP_EOL;
363
            $name = 'UNKNOWN';
364
          }
365
366
          // format filename of contact photo; remove special letters
367
          if($vcard_obj->photo)
368
          {
369
            $photo = str_replace(array(',', '&', ' ', '/', 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü', 'ß', 'á', 'à', 'ó', 'ò', 'ú', 'ù', 'í', 'ø'),
370
            array('', '_', '_', '_', 'ae', 'oe', 'ue', 'Ae', 'Oe', 'Ue', 'ss', 'a', 'a', 'o', 'o', 'u', 'u', 'i', 'oe'), $name);
371
          }
372
          else
373
            $photo = '';
374
375
          // phone
376
          $phone_no = array();
377
          if($vcard_obj->categories)
378
            $categories = $vcard_obj->categories[0];
379
          else
380
            $categories = array();
381
382
          // check for quickdial entry
383
          if(isset($vcard_obj->note[0]))
384
          {
385
            $note = $vcard_obj->note[0];
386
            $notes = explode($this->config['quickdial_keyword'], $note);
387
            foreach($notes as $linenr => $linecontent)
388
            {
389
              $found = strrpos($linecontent, ":**7");
390
              if($found > 0)
391
              {
392
                $pos_qd_start = strrpos($linecontent, ":**7");
393
                $quick_dial_for_nr = preg_replace("/[^0-9+]/", "", substr($linecontent, 0, $pos_qd_start));
394
                $quick_dial_nr = intval(substr($linecontent, $pos_qd_start + 4, 3));
395
                $quick_dial_arr[$quick_dial_for_nr] = $quick_dial_nr;
396
              }
397
            }
398
          }
399
400
          // e-mail addresses
401
          $email_add = array();
402
          $vip = isset($this->config['group_vip']) && in_array((string)$this->config['group_vip'], $categories);
403
404
          if(array_key_exists('group_filter', $this->config) && is_array($this->config['group_filter']))
405
          {
406
            $add_entry = 0;
407
            foreach($this->config['group_filter'] as $group_filter)
408
            {
409
              if(in_array($group_filter, $categories))
410
              {
411
                $add_entry = 1;
412
                break;
413
              }
414
            }
415
          } 
416
          else
417
            $add_entry = 1;
418
419
          if($add_entry == 1)
420
          {
421
            foreach($vcard_obj->tel as $t)
422
            {
423
              $prio = 0;
424
              $quickdial = null;
425
              
426
              if(!is_array($t) || empty($t['type']))
427
              {
428
                $type = "mobile";
429
                $phone_number = $t;
430
              }
431
              else
432
              {
433
                $phone_number = $t['value'];
434
                
435
                $phone_number_clean = preg_replace("/[^0-9+]/", "", $phone_number);
436
                foreach($quick_dial_arr as $qd_phone_nr => $value)
437
                {
438
                  if($qd_phone_nr == $phone_number_clean)
439
                  {
440
                    //Set quickdial
441
                    if($value == 1)
442
                      print "\nWARNING: Quickdial value 1 (**701) is not possible but used! \n";
443
                    elseif($value >= 100)
444
                      print "\nWARNING: Quickdial value bigger than 99 (**799) is not possible but used! \n";
445
446
                    $quickdial = $value;
447
                  }
448
                }
449
450
                $typearr_lower = unserialize(strtolower(serialize($t['type'])));
451
452
                // find out priority
453
                if(in_array("pref", $typearr_lower))
454
                  $prio = 1;
455
456
                // set the proper type
457
                if(in_array("cell", $typearr_lower))
458
                  $type = "mobile";
459
                elseif(in_array("home", $typearr_lower))
460
                  $type = "home";
461
                elseif(in_array("fax", $typearr_lower))
462
                  $type = "fax_work";
463
                elseif(in_array("work", $typearr_lower))
464
                  $type = "work";
465
                elseif(in_array("other", $typearr_lower))
466
                  $type = "other";
467
                elseif(in_array("dom", $typearr_lower))
468
                  $type = "other";
469
                else
470
                  continue;
471
              }
472
              $phone_no[] = array("type"=>$type, "prio"=>$prio, "quickdial"=>$quickdial, "value" => $this->_clear_phone_number($phone_number));
473
            }
474
475
            // request email address and type
476
            if($vcard_obj->email)
477
            {
478
              foreach($vcard_obj->email as $e)
479
              {
480
                if(empty($e['type']))
481
                {
482
                  $type_email = "work";
483
                  $email = $e;
484
                }
485
                else
486
                {
487
                  $email = $e['value'];
488
                  $typearr_lower = unserialize(strtolower(serialize($e['type'])));
489
                  if(in_array("work", $typearr_lower))
490
                    $type_email = "work";
491
                  elseif(in_array("home", $typearr_lower))
492
                    $type_email = "home";
493
                  elseif(in_array("other", $typearr_lower))
494
                    $type_email = "other";
495
                  else
496
                    continue;
497
                }
498
499
                // DEBUG: print out the email address on the console
500
                //print $type_email.": ".$email."\n";
501
502
                $email_add[] = array("type"=>$type_email, "value" => $email);
503
              }
504
            }
505
            $entries[] = array("realName" => $name, "telephony" => $phone_no, "email" => $email_add, "vip" => $vip, "photo" => $photo, "photo_data" => $vcard_obj->photo);
506
          }
507
        }
508
509
        $snum++;
510
      }
511
    }
512
513
    $this->entries = $entries;
514
  }
515
516
  private function _clear_phone_number($number)
517
  {
518
    return preg_replace("/[^0-9+]/", "", $number);
519
  }
520
521
  public function build_fb_xml()
522
  {
523
    if(empty($this->entries))
524
      throw new Exception('No entries available! Call get_carddav_entries or set $this->entries manually!');
525
526
    // create FB XML in utf-8 format
527
    $root = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><phonebooks><phonebook></phonebook></phonebooks>');
528
    $pb = $root->phonebook;
529
    $pb->addAttribute("name", $this->config['phonebook_name']);
530
531
    foreach($this->entries as $entry)
532
    {
533
      $contact = $pb->addChild("contact");
534
      $contact->addChild("category", $entry['vip']);
535
      $person = $contact->addChild("person");
536
      $person->addChild("realName", $this->_convert_text($entry['realName']));
537
538
      echo " VCard: '" . utf8_decode($entry['realName']) . "'" . PHP_EOL;
539
540
      // telephone: put the phonenumbers into the fritzbox xml file
541
      $telephony = $contact->addChild("telephony");
542
      $id = 0;
543
      foreach($entry['telephony'] as $tel)
544
      {
545
        $num = $telephony->addChild("number", $tel['value']);
546
        $num->addAttribute("type", $tel['type']);
547
        $num->addAttribute("vanity", "");
548
        $num->addAttribute("prio", $tel['prio']);
549
        $num->addAttribute("id", $id);
550
551
        if(isset($tel['quickdial']))
552
        {
553
          $num->addAttribute("quickdial", $tel['quickdial']);
554
          print "  Added quickdial: " . $tel['quickdial'] . " for: " . $tel['value'] . " (" . $tel['type'] . ")" . PHP_EOL;
555
        }
556
557
        $id++;
558
        print "  Added phone: " . $tel['value'] . " (" . $tel['type'] . ")" . PHP_EOL;
559
      }
560
561
      // output a warning if no telephone number was found
562
      if($id == 0)
563
        print "  WARNING: no phone entry found. VCard will be ignored." . PHP_EOL;
564
565
      // email: put the email addresses into the fritzbox xml file
566
      $email = $contact->addChild("services");
567
      $id = 0;
568
      foreach($entry['email'] as $mail)
569
      {
570
        $mail_adr = $email->addChild("email", $mail['value']);
571
        $mail_adr->addAttribute("classifier", $mail['type']);
572
        $mail_adr->addAttribute("id", $id);
573
        $id++;
574
575
        print "  Added email: " . $mail['value'] . " (" . $mail['type'] . ")" . PHP_EOL;
576
      }
577
578
      // check for a photo being part of the VCard
579
      if(($entry['photo']) and ($entry['photo_data']) and (is_array($entry['photo_data'])) and ($entry['photo_data'][0]))
580
      {
581
        // check if 'photo_data'[0] is an array as well because then
582
        // we have to extract ['value'] and friends.
583
        if(is_array($entry['photo_data'][0]) and (array_key_exists('value', $entry['photo_data'][0])))
584
        {
585
          // check if photo_data really contains JPEG data
586
          if((array_key_exists('type', $entry['photo_data'][0])) and (is_array($entry['photo_data'][0]['type'])) and
587
             ($entry['photo_data'][0]['type'][0] == 'jpeg' or $entry['photo_data'][0]['type'][0] == 'jpg'))
588
          {
589
            // get photo, rename, base64 convert and save as jpg
590
            $photo_data = $entry['photo_data'][0]['value'];
591
            $photo_version = substr(sha1($photo_data), 0, 5);
592
            $photo_file = $this->tmpdir . '/' . "{$entry['photo']}_{$photo_version}.jpg";
593
594
            // check for base64 encoding of the photo data and convert it
595
            // accordingly.
596
            if(((array_key_exists('encoding', $entry['photo_data'][0])) and ($entry['photo_data'][0]['encoding'] == 'b')) or $this->is_base64($photo_data))
597
            {
598
              file_put_contents($photo_file . ".b64", $photo_data);
599
              $this->base64_to_jpeg($photo_file . ".b64", $photo_file);
600
              unlink($photo_file . ".b64");
601
            }
602
            else
603
            {
604
              print "  WARNING: non-base64 encoded photo data found and used." . PHP_EOL;
605
              file_put_contents($photo_file, $photo_data);
606
            }
607
608
            // add contact photo to xml
609
            $person->addChild("imageURL", $this->config['fritzbox_path'] . $this->config['usb_disk'] . "FRITZ/fonpix/" . basename($photo_file));
610
611
            print "  Added photo: " . basename($photo_file) . PHP_EOL;
612
          }
613
          else
614
           print "  WARNING: Only jpg contact photos are currently supported." . PHP_EOL;
615
        }
616
        elseif(substr($entry['photo_data'][0], 0, 4) == 'http')
617
        {
618
          // add contact photo to xml
619
          $person->addChild("imageURL", $entry['photo_data'][0]);
620
621
          print "  Added photo: " . $entry['photo_data'][0] . PHP_EOL;
622
        }
623
        else
624
          print "  WARNING: Only VCard embedded photo data or a reference URL is currently supported." . PHP_EOL;
625
      }
626
627
      $contact->addChild("services");
628
      $contact->addChild("setup");
629
      $contact->addChild("mod_time", (string)time());
630
    }
631
632
    if($root->asXML() !== false)
633
      $this->fbxml = $root->asXML();
634
    else
635
    {
636
      print "  ERROR: created XML data isn't well-formed." . PHP_EOL;
637
      exit(1);
638
    }
639
  }
640
641
  public function _convert_text($text)
642
  {
643
    $text = htmlspecialchars($text);
644
    return $text;
645
  }
646
647
  public function _concat($text1, $text2)
648
  {
649
    if($text1 == '')
650
      return $text2;
651
    elseif($text2 == '')
652
      return $text1;
653
    else
654
      return $text1 . ", " . $text2;
655
  }
656
657
  public function _parse_fb_result($text)
658
  {
659
    if(preg_match("/\<h2\>([^\<]+)\<\/h2\>/", $text, $matches) == 1 && !empty($matches))
660
      return $matches[1];
661
    else
662
      return "Error while uploading xml to fritzbox";
663
  }
664
665
  public function upload_to_fb()
666
  {
667
    // if the user wants to save the xml to a separate file, we do so now
668
    if(array_key_exists('output_file', $this->config))
669
    {
670
      // build md5 hash of previous stored xml without <mod_time> Elements
671
      $oldphonebhash = md5(preg_replace("/<mod_time>(\\d{10})/","",file_get_contents($this->config['output_file'],'r'),-1,$debugoldtsreplace));
672
      $output = fopen($this->config['output_file'], 'w');
673
      if($output)
674
      {
675
        fwrite($output, $this->fbxml);
676
        fclose($output);
677
        print " Saved to file " . $this->config['output_file'] . PHP_EOL;
678
      }
679
	  if (array_key_exists('output_and_upload', $this->config) and $this->config['output_and_upload'])
680
	  {
681
	  	$newphonebhash = md5(preg_replace("/<mod_time>(\\d{10})/","",file_get_contents($this->config['output_file'],'r'),-1,$debugnewtsreplace));
682
	  	print " INFO: Compare old and new phonebook file versions." . PHP_EOL . " INFO: old version: " . $oldphonebhash . PHP_EOL . " INFO: new version: " . $newphonebhash . PHP_EOL;
683
	  	if($oldphonebhash === $newphonebhash)
684
      	{
685
      	print " INFO: Same versions ==> No changes in phonebook or images" . PHP_EOL . " EXIT: No need to upload phonebook to the FRITZ!Box.". PHP_EOL;
686
      	return 0;
687
      	}
688
      	else
689
      	print " INFO: Different versions ==> Changes in phonebook." . PHP_EOL . " INFO: Changes dedected! Continue with upload." . PHP_EOL;
690
      }
691
	  else
692
      return 0;  
693
    }
694
    // now we upload the photo jpgs first being stored in the
695
    // temp directory.
696
697
    // perform an ftps-connection to copy over the photos to a specified directory
698
    $ftp_server = $this->config['fritzbox_ip_ftp'];
699
    $conn_id = ftp_ssl_connect($ftp_server);
700 View Code Duplication
    if($conn_id == false)
701
    {
702
      print " WARNING: Secure connection to FTP-server '" . $ftp_server . "' failed, retrying without SSL." . PHP_EOL;
703
      $conn_id = ftp_connect($ftp_server);
704
    }
705
706
    if($conn_id != false)
707
    {
708
      ftp_set_option($conn_id, FTP_TIMEOUT_SEC, 60);
709
      $login_result = ftp_login($conn_id, $this->config['fritzbox_user'], $this->config['fritzbox_pw']);
710
      if($login_result === true)
711
      {
712
        ftp_pasv($conn_id, true);
713
714
        // create remote photo path on FRITZ!Box if it doesn't exist
715
        $remote_path = $this->config['usb_disk'] . "/FRITZ/fonpix";
716
        $all_existing_files = ftp_nlist($conn_id, $remote_path);
717
        if($all_existing_files == false)
718
        {
719
          ftp_mkdir($conn_id, $remote_path);
720
          $all_existing_files = array();
721
        }
722
723
        // now iterate through all jpg files in tempdir and upload them if necessary
724
        $dir = new DirectoryIterator($this->tmpdir);
725
        foreach($dir as $fileinfo)
726
        {
727
          if(!$fileinfo->isDot())
728
          {
729
            if($fileinfo->getExtension() == "jpg")
730
            {
731
              $file = $fileinfo->getFilename();
732
733
              print " FTP-Upload '" . $file . "'...";
734
              if(!in_array($remote_path . "/" . $file, $all_existing_files))
735
              {
736
                if(!ftp_put($conn_id, $remote_path . "/" . $file, $fileinfo->getPathname(), FTP_BINARY))
737
                {
738
                  // retry when a fault occurs.
739
                  print " retrying... ";
740
                  $conn_id = ftp_ssl_connect($ftp_server);
741 View Code Duplication
                  if($conn_id == false)
742
                  {
743
                    print " WARNING: Secure re-connection to FTP-server '" . $ftp_server . "' failed, retrying without SSL." . PHP_EOL;
744
                    $conn_id = ftp_connect($ftp_server);
745
                  }
746
747
                  if($conn_id == false)
748
                  {
749
                    print " ERROR: couldn't re-connect to FTP server '" . $ftp_server . "', abortіng." . PHP_EOL;
750
                    break;
751
                  }
752
753
                  $login_result = ftp_login($conn_id, $this->config['fritzbox_user'], $this->config['fritzbox_pw']);
754
                  if($login_result === false)
755
                  {
756
                    print " ERROR: couldn't re-login to FTP-server '" . $ftp_server . "' with provided username/password settings." . PHP_EOL;
757
                    break;
758
                  }
759
760
                  ftp_pasv($conn_id, true);
761
                  if(!ftp_put($conn_id, $remote_path . "/" . $file, $fileinfo->getPathname(), FTP_BINARY))
762
                    print " ERROR: while uploading file " . $fileinfo->getFilename() . PHP_EOL;
763
                  else
764
                    print " ok." . PHP_EOL;
765
                }
766
                else
767
                  print " ok." . PHP_EOL;
768
769
                // cleanup old files
770
                foreach($all_existing_files as $existing_file)
771
                {
772
                  if(strpos($existing_file, $remote_path . "/" . substr($file, 0, -10)) !== false)
773
                  {
774
                    print " FTP-Delete: " . $existing_file . PHP_EOL;
775
                    ftp_delete($conn_id, $remote_path . "/" . basename($existing_file));
776
                  }
777
                }
778
              }
779
              else
780
                print " already exists." . PHP_EOL;
781
            }
782
          }
783
        }
784
      }
785
      else
786
        print " ERROR: couldn't login to FTP-server '" . $ftp_server . "' with provided username/password settings." . PHP_EOL;
787
788
      // close ftp connection
789
      ftp_close($conn_id);
790
    }
791
    else
792
      print " ERROR: couldn't connect to FTP server '" . $ftp_server . "'." . PHP_EOL;
793
    
794
    // lets post the phonebook xml to the FRITZ!Box
795
    print " Uploading Phonebook XML to " . $this->config['fritzbox_ip'] . PHP_EOL;
796
    try
797
    {
798
      $fritz = new fritzbox_api($this->config['fritzbox_pw'],
799
        $this->config['fritzbox_user'],
800
        $this->config['fritzbox_ip'],
801
        $this->config['fritzbox_force_local_login']);
802
803
      $formfields = array(
804
        'PhonebookId' => $this->config['phonebook_number']
805
      );
806
807
      $filefileds = array('PhonebookImportFile' => array(
808
       'type' => 'text/xml',
809
       'filename' => 'updatepb.xml',
810
       'content' => $this->fbxml,
811
       )
812
      );
813
814
      $raw_result = $fritz->doPostFile($formfields, $filefileds); // send the command
815
      $msg = $this->_parse_fb_result($raw_result);
816
      unset($fritz); // destroy the object to log out
817
818
      print "  FRITZ!Box returned message: '" . $msg . "'" . PHP_EOL;
819
    }
820
    catch(Exception $e)
821
    {
822
      print "  ERROR: " . $e->getMessage() . PHP_EOL; // show the error message in anything failed
823
    }
824
  }
825
}
826