Overview

Packages

  • Horde
    • Data
  • None
  • Turba

Classes

  • Turba_Data_Ldif
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde_Data implementation for LDAP Data Interchange Format (LDIF).
  4:  *
  5:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file LICENSE for license information (ASL).  If you
  8:  * did not receive this file, see http://www.horde.org/licenses/apache.
  9:  *
 10:  * @author  Rita Selsky <ritaselsky@gmail.com>
 11:  * @package Horde_Data
 12:  */
 13: class Turba_Data_Ldif extends Horde_Data_Base
 14: {
 15:     protected $_extension = 'ldif';
 16: 
 17:     protected $_contentType = 'text/ldif';
 18: 
 19:     /**
 20:      * Useful Mozilla address book attribute names.
 21:      *
 22:      * @var array
 23:      */
 24:     private $_mozillaAttr = array('cn', 'givenName', 'sn', 'mail', 'mozillaNickname',
 25:                              'homeStreet', 'mozillaHomeStreet2', 'mozillaHomeLocalityName',
 26:                              'mozillaHomeState', 'mozillaHomePostalCode',
 27:                              'mozillaHomeCountryName', 'street',
 28:                              'mozillaWorkStreet2', 'l', 'st', 'postalCode',
 29:                              'c', 'homePhone', 'telephoneNumber', 'mobile',
 30:                              'fax', 'title', 'company', 'description', 'mozillaWorkUrl',
 31:                               'department', 'mozillaNickname');
 32: 
 33:     /**
 34:      * Useful Turba address book attribute names.
 35:      *
 36:      * @var array
 37:      */
 38:     private $_turbaAttr = array('name', 'firstname', 'lastname', 'email', 'alias',
 39:                             'homeAddress', 'homeStreet', 'homeCity',
 40:                             'homeProvince', 'homePostalCode', 'homeCountry',
 41:                             'workAddress', 'workStreet', 'workCity', 'workProvince',
 42:                             'workPostalCode', 'workCountry',
 43:                             'homePhone', 'workPhone', 'cellPhone',
 44:                             'fax', 'title', 'company', 'notes', 'website',
 45:                             'department', 'nickname');
 46:     /**
 47:      * Turba address book attribute names and the corresponding Mozilla name.
 48:      *
 49:      * @var array
 50:      */
 51:     private $_turbaMozillaMap = array('name' => 'cn',
 52:                                   'firstname' => 'givenName',
 53:                                   'lastname' => 'sn',
 54:                                   'email' => 'mail',
 55:                                   'alias' => 'mozillaNickname',
 56:                                   'homePhone' => 'homePhone',
 57:                                   'workPhone' => 'telephoneNumber',
 58:                                   'cellPhone' => 'mobile',
 59:                                   'fax' => 'fax',
 60:                                   'title' => 'title',
 61:                                   'company' => 'company',
 62:                                   'notes' => 'description',
 63:                                   'homeAddress' => 'homeStreet',
 64:                                   'homeStreet' => 'mozillaHomeStreet2',
 65:                                   'homeCity' => 'mozillaHomeLocalityName',
 66:                                   'homeProvince' => 'mozillaHomeState',
 67:                                   'homePostalCode' => 'mozillaHomePostalCode',
 68:                                   'homeCountry' => 'mozillaHomeCountryName',
 69:                                   'workAddress' => 'street',
 70:                                   'workStreet' => 'mozillaWorkStreet2',
 71:                                   'workCity' => 'l',
 72:                                   'workProvince' => 'st',
 73:                                   'workPostalCode' => 'postalCode',
 74:                                   'workCountry' => 'c',
 75:                                   'website' => 'mozillaWorkUrl',
 76:                                   'department' => 'department',
 77:                                   'nickname' => 'mozillaNickname');
 78: 
 79:     /**
 80:      * Check if a string is safe according to RFC 2849, or if it needs to be
 81:      * base64 encoded.
 82:      *
 83:      * @private
 84:      *
 85:      * @param string $str  The string to check.
 86:      *
 87:      * @return boolean  True if the string is safe, false otherwise.
 88:      */
 89:     private function _is_safe_string($str)
 90:     {
 91:         /*  SAFE-CHAR         = %x01-09 / %x0B-0C / %x0E-7F
 92:          *                     ; any value <= 127 decimal except NUL, LF,
 93:          *                     ; and CR
 94:          *
 95:          *  SAFE-INIT-CHAR    = %x01-09 / %x0B-0C / %x0E-1F /
 96:          *                     %x21-39 / %x3B / %x3D-7F
 97:          *                     ; any value <= 127 except NUL, LF, CR,
 98:          *                     ; SPACE, colon (":", ASCII 58 decimal)
 99:          *                     ; and less-than ("<" , ASCII 60 decimal) */
100:         if (!strlen($str)) {
101:             return true;
102:         }
103:         if ($str[0] == ' ' || $str[0] == ':' || $str[0] == '<') {
104:             return false;
105:         }
106:         for ($i = 0; $i < strlen($str); ++$i) {
107:             if (ord($str[$i]) > 127 || $str[$i] == NULL || $str[$i] == "\n" ||
108:                 $str[$i] == "\r") {
109:                 return false;
110:             }
111:         }
112: 
113:         return true;
114:     }
115: 
116:     public function importData($contents, $header = false)
117:     {
118:         $data = array();
119:         $records = preg_split('/(\r?\n){2}/', $contents);
120:         foreach ($records as $record) {
121:             if (trim($record) == '') {
122:                 /* Ignore empty records */
123:                 continue;
124:             }
125:             /* one key:value pair per line */
126:             $lines = preg_split('/\r?\n/', $record);
127:             $hash = array();
128:             foreach ($lines as $line) {
129:                 // [0] = key, [1] = delimiter, [2] = value
130:                 $res = preg_split('/(:[:<]?) */', $line, 2, PREG_SPLIT_DELIM_CAPTURE);
131:                 if ((count($res) == 3) &&
132:                     in_array($res[0], $this->_mozillaAttr)) {
133:                     $hash[$res[0]] = ($res[1] == '::')
134:                         ? base64_decode($res[2])
135:                         : $res[2];
136:                 }
137:             }
138:             $data[] = $hash;
139:         }
140: 
141:         return $data;
142:     }
143: 
144:     /**
145:      * Builds a LDIF file from a given data structure and triggers its download.
146:      * It DOES NOT exit the current script but only outputs the correct headers
147:      * and data.
148:      *
149:      * @param string $filename  The name of the file to be downloaded.
150:      * @param array $data       A two-dimensional array containing the data
151:      *                          set.
152:      * @param boolean $header   If true, the rows of $data are associative
153:      *                          arrays with field names as their keys.
154:      */
155:     public function exportFile($filename, $data, $header = false)
156:     {
157:         $export = $this->exportData($data, $header);
158:         $GLOBALS['browser']->downloadHeaders($filename, 'text/ldif', false, strlen($export));
159:         echo $export;
160:     }
161: 
162:     /**
163:      * Builds a LDIF file from a given data structure and returns it as a
164:      * string.
165:      *
166:      * @param array $data      A two-dimensional array containing the data set.
167:      * @param boolean $header  If true, the rows of $data are associative
168:      *                         arrays with field names as their keys.
169:      *
170:      * @return string  The LDIF data.
171:      */
172:     public function exportData($data, $header = false)
173:     {
174:         if (!is_array($data) || !count($data)) {
175:             return '';
176:         }
177:         $export = '';
178:         $mozillaTurbaMap = array_flip($this->_turbaMozillaMap) ;
179:         foreach ($data as $row) {
180:             $recordData = '';
181:             foreach ($this->_mozillaAttr as $value) {
182:                 if (isset($row[$mozillaTurbaMap[$value]])) {
183:                     // Base64 encode each value as necessary and store it.
184:                     // Store cn and mail separately for use in record dn
185:                     if (!$this->_is_safe_string($row[$mozillaTurbaMap[$value]])) {
186:                         $recordData .= $value . ':: ' . base64_encode($row[$mozillaTurbaMap[$value]]) . "\n";
187:                     } else {
188:                         $recordData .= $value . ': ' . $row[$mozillaTurbaMap[$value]] . "\n";
189:                     }
190:                 }
191:             }
192: 
193:             $dn = 'cn=' . $row[$mozillaTurbaMap['cn']] . ',mail=' . $row[$mozillaTurbaMap['mail']];
194:             if (!$this->_is_safe_string($dn)) {
195:                 $export .= 'dn:: ' . base64_encode($dn) . "\n";
196:             } else {
197:                 $export .= 'dn: ' . $dn . "\n";
198:             }
199: 
200:             $export .= "objectclass: top\n"
201:                 . "objectclass: person\n"
202:                 . "objectclass: organizationalPerson\n"
203:                 . "objectclass: inetOrgPerson\n"
204:                 . "objectclass: mozillaAbPersonAlpha\n"
205:                 . $recordData . "modifytimestamp: 0Z\n\n";
206:         }
207: 
208:         return $export;
209:     }
210: 
211:     /**
212:      * Takes all necessary actions for the given import step, parameters and
213:      * form values and returns the next necessary step.
214:      *
215:      * @param integer $action  The current step. One of the IMPORT_* constants.
216:      * @param array $param     An associative array containing needed
217:      *                         parameters for the current step.
218:      *
219:      * @return mixed  Either the next step as an integer constant or imported
220:      *                data set after the final step.
221:      * @throws Horde_Data_Exception
222:      */
223:     public function nextStep($action, $param = array())
224:     {
225:         switch ($action) {
226:         case Horde_Data::IMPORT_FILE:
227:             parent::nextStep($action, $param);
228: 
229:             $f_data = $this->importFile($_FILES['import_file']['tmp_name']);
230: 
231:             $data = array();
232:             foreach ($f_data as $record) {
233:                 $turbaHash = array();
234:                 foreach ($this->_turbaAttr as $value) {
235:                     switch ($value) {
236:                     case 'homeAddress':
237:                         // These are the keys we're interested in.
238:                         $keys = array('homeStreet', 'mozillaHomeStreet2',
239:                                       'mozillaHomeLocalityName', 'mozillaHomeState',
240:                                       'mozillaHomePostalCode', 'mozillaHomeCountryName');
241: 
242:                         // Grab all of them that exist in $record.
243:                         $values = array_intersect_key($record, array_flip($keys));
244: 
245:                         // Special handling for State if both State
246:                         // and Locality Name are set.
247:                         if (isset($values['mozillaHomeLocalityName'])
248:                             && isset($values['mozillaHomeState'])) {
249:                             $values['mozillaHomeLocalityName'] .= ', ' . $values['mozillaHomeState'];
250:                             unset($values['mozillaHomeState']);
251:                         }
252: 
253:                         if ($values) {
254:                             $turbaHash[$value] = implode("\n", $values);
255:                         }
256:                         break;
257: 
258:                     case 'workAddress':
259:                         // These are the keys we're interested in.
260:                         $keys = array('street', 'mozillaWorkStreet2', 'l',
261:                                       'st', 'postalCode', 'c');
262: 
263:                         // Grab all of them that exist in $record.
264:                         $values = array_intersect_key($record, array_flip($keys));
265: 
266:                         // Special handling for "st" if both "st" and
267:                         // "l" are set.
268:                         if (isset($values['l']) && isset($values['st'])) {
269:                             $values['l'] .= ', ' . $values['st'];
270:                             unset($values['st']);
271:                         }
272: 
273:                         if ($values) {
274:                             $turbaHash[$value] = implode("\n", $values);
275:                         }
276:                         break;
277: 
278:                     default:
279:                         if (isset($record[$this->_turbaMozillaMap[$value]])) {
280:                             $turbaHash[$value] = $record[$this->_turbaMozillaMap[$value]];
281:                         }
282:                         break;
283:                     }
284:                 }
285: 
286:                 $data[] = $turbaHash;
287:             }
288: 
289:             $GLOBALS['session']->remove('horde', 'import_data/data');
290:             return $data;
291: 
292:         default:
293:             return parent::nextStep($action, $param);
294:         }
295:     }
296: 
297: }
298: 
API documentation generated by ApiGen