diff --git a/.gitignore b/.gitignore index 3bdc4e066866d0433aafc3341fa4b43b0b420b58..6e7535b0b92c9fd5d8c741d3c95ba4fe7c19630e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Vagrantfile # Staging directory used for packaging script stage + +# Ignore packaged plugins and language packs +*.phar diff --git a/WHATSNEW.md b/WHATSNEW.md index 19c3cbb87e562594206bb52c85b04b9cb02c0545..b361dbc8968dd394620a8842b4133bdb827ae104 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -1,3 +1,47 @@ +osTicket v1.8.1.2 +================= +* All fixes and enhancements from v1.8.0.4, plus * + +### Enhancements + * Better detection of email loops (#584, #684) + +### Bugs + * Fix selection of the auto-response email for a department (#666) + * Don't require current password when resetting (#671) + * Fix incorrect matchup of collaborators to users (#676) + +osTicket v1.8.1.1 +================= +* All fixes and enahncements from v1.8.0.4, plus * + +### Enhancements + * Add signature to activity notice for staff replies (#605) + * Show company name in the copyright footer (#586) + * Signature is displayed below the staff response box (#609) + +### Bugs + * Fix footnotes generated in html2text for same link text but different URLs (5e2f58d) + * Fix processing of emails for existing users (#588) + * Avoid adding an aliased system email address as a collaborator (#604, #627) + * Show current staff / user names where possible (#608) + * Fix display of _forgot my password_ link (#611) + * Export the value of custom fields (not the ID number) (#610) + * Fix saving the backend with file metadata (#595) + * Use the database as a failsafe attachment backend (#594) + * Avoid a crash when sending some mails (#589) + * Fix migrating attachments when upgrading from osTicket 1.6 (#614) + * Email templates ship with the ticket number in the subject line (#644) + * If inline images are stripped from the email, they are not considered attachments (#649, bcbebd0, 35a23be) + * Fix incorrect Content-Id headers generated for inline images (23ce0a0, e37ec74) + * New installs have the `Staff` group enabled (c7130c5) + * Always show the ticket thread when following an email link (17725ca) + +### Security and Performance + * Staff can only see closed tickets if they have access via group or primary department (#623, #655) + * Fix incorrect honoring of ban list and over limit settings (#660) + * Keep existing session after login (c4bfb69) + * Fix password reset system (dfaca0d, #664) + osTicket v1.8.0.4 ================= ### Enhancements @@ -36,6 +80,31 @@ osTicket v1.8.0.4 ### Performance and Security * Staff can only see closed tickets if they have access via group or primary department (#623, #655) +osTicket v1.8.1 +=============== +*All fixes and enhancements from v1.8.0.3, plus* + +### Enhancements + * Ticket filters support matching on email To and Cc fields (#529) + * Popup summary and collaborator list on ticket queue page (#521) + +### Bugs + * New ticket by staff adds `recipient` and `staff` context to email templates (#527) + * Forbid password reset for non-local users (#570) + * Allow an administrator to lift the force password change flag (#570) + * Locks are released on logout (#522) + * Text email footnotes are written as [title][#] (7ccbf0c) + * Fix several issues with display and download of attachments (#530) + * Fix sending a reply email if requested not to (#531) + * Only consider collaborators if the receiving system email is identified (#537) + * Do not consider `delivered-to` addresses as collaborators (#544) + * Assume `iso-8859-1` MIME body encoding if not specified (#551) + * Add new features to the storage API to implement Amazon S3 (#515) + +### Performance and Security + * Support auditing login attempts (#559) + * Avoid auth strikeouts when not attempting a login (#559, #523) + osTicket v1.8.0.3 ================= ### Enhancements @@ -75,6 +144,78 @@ osTicket v1.8.0.3 * Reuse SMTP connections where possible (#462) * Enforce max file size for attachments sent via API (#568) +osTicket v1.8.1-rc1 +=================== +### Enhancements + * Much better email bounce detection (#461, #474) + * Handle messages forwarded as `message/rfc822` content type (#482) + * [Esc] key cancels popup dialogs (#465) + * Support regex matches in ticket filter (584465c) + +### Bugs + * 'Priority' column is included in the ticket queue export (#451) + * Retry queries on MySQL error 1213 (#493) + * Client login email is not case-sensitive (398cbc7) + * Drop silly border on text emails if HTML ticket thread is disabled (439a32a) + * Fix ticket submission error if client is already logged in (#469) + * Fix fetching from more than 10 mail accounts (#468) + * Fix `deploy` command-line application (#450) + * Fix error email on upgrade (#452) + * Ship with a `plugins/` folder (90b0a65) + * Fix file key not replaced in thread body correctly for de-duplicated files (#492) + * Better handling of text and html thread posts (#508) + * Fix clickable links ending with punctuation (#497) + * Fix whitespace mangling of Unicode text with non-breaking-spaces (#502) + * Fix image size set to zero when images are added to drafts (#504) + * Correctly detect php-dom extension (#503) + * Fix delivery issue of emails delivered to group mailboxes (#510) + +### Merged from v1.8.0.2 + * Log entry for password reset attempts (#435) + +osTicket v1.8.1 (Preview) +========================= +### Collaborator Support (CC) +In addition to the ticket owner, other end users can be collaborators on a +ticket. Responses received from them are integrated automatically into the +ticket thread, and emails are sent to all collaborators when new messages and +responses arrive into the system. All collaborators have access to the ticket +via the client portal and are able to log new messages. + +### Plugin management system +osTicket supports plugins via a (currently undocumented) simple plugin API and +interface. Plugins can be written and distributed as files or unpacked via ZIP +archives, or distributed via PHP PHAR files. The plugin system is developed in +hopes of adding extensibility to osTicket without significant overhead. +Initially, two "classes" of plugins are supported: authentication, and file +storage. + +### Pluggable authentication +Staff members can now be authenticated against a backend other than the +osTicket internal database. Available immediately is integration with LDAP +(RFC-2307) and Microsoft® Active Directory. The initial authentication system +also support user lookups, so when browsing for new users when creating +tickets, your directory server will be queried for users and email addresses. + +### Pluggable attachment storage +Attachments can live outside the database again. You can now write or install a +plugin to store your attachments somewhere other than in your database, and +osTicket will use the backend to store and retrieve (or redirect to) your +attachments. We've initially made a plugin available to store attachments on +the filesystem and plan on adding an Amazon S3 plugin very soon. + +### Internationalization, Phase 1 +Select your default data on installation, and select the language preference, +as a staff member, for the help tips. You can also now select the language of +the email templates when creating a new template. The templates for that +language will be used instead of the English ones where translated versions are +available. + +### Minor Enhancements + * Clients can update their profile information on the web portal + * Clients can update ticket details (if enabled) + * Custom ticket-details fields are included in ticket queue exports + osTicket v1.8.0.2 ================= ### Enhancements diff --git a/ajax.php b/ajax.php index 57b6274ccee12f4292f97aa7df5b4f9c9cd938ce..3374776c7a54ef4cbaa1c0bf7a8139d2179be2dc 100644 --- a/ajax.php +++ b/ajax.php @@ -39,5 +39,6 @@ $dispatcher = patterns('', url_get('^help-topic/(?P<id>\d+)$', 'getClientFormsForHelpTopic') )) ); +Signal::send('ajax.client', $dispatcher); print $dispatcher->resolve($ost->get_path_info()); ?> diff --git a/attachment.php b/attachment.php index d780ae2da4187c41a0607afa30267504c7597f2c..73dbfd710b9a58f164034d35239842f01a102f17 100644 --- a/attachment.php +++ b/attachment.php @@ -17,18 +17,18 @@ require('secure.inc.php'); require_once(INCLUDE_DIR.'class.attachment.php'); //Basic checks -if(!$thisclient - || !$_GET['id'] +if(!$thisclient + || !$_GET['id'] || !$_GET['h'] || !($attachment=Attachment::lookup($_GET['id'])) || !($file=$attachment->getFile())) die('Unknown attachment!'); //Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!! -$vhash=md5($attachment->getFileId().session_id().$file->getHash()); -if(strcasecmp(trim($_GET['h']),$vhash) - || !($ticket=$attachment->getTicket()) - || !$ticket->checkClientAccess($thisclient)) +$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey())); +if(strcasecmp(trim($_GET['h']),$vhash) + || !($ticket=$attachment->getTicket()) + || !$ticket->checkUserAccess($thisclient)) die('Unknown or invalid attachment'); //Download the file.. $file->download(); diff --git a/bootstrap.php b/bootstrap.php index ec2299b6c4e19e99d4206db574e2ebd28f7e5d07..0a50c4a2635b693e575d87a3a13dda9f66e3a997 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -89,6 +89,7 @@ class Bootstrap { define('TICKET_LOCK_TABLE',$prefix.'ticket_lock'); define('TICKET_EVENT_TABLE',$prefix.'ticket_event'); define('TICKET_EMAIL_INFO_TABLE',$prefix.'ticket_email_info'); + define('TICKET_COLLABORATOR_TABLE', $prefix.'ticket_collaborator'); define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority'); define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE); @@ -111,6 +112,8 @@ class Bootstrap { define('FILTER_TABLE', $prefix.'filter'); define('FILTER_RULE_TABLE', $prefix.'filter_rule'); + define('PLUGIN_TABLE', $prefix.'plugin'); + define('API_KEY_TABLE',$prefix.'api_key'); define('TIMEZONE_TABLE',$prefix.'timezone'); } @@ -169,8 +172,8 @@ class Bootstrap { function loadCode() { #include required files - require(INCLUDE_DIR.'class.ostsession.php'); - require(INCLUDE_DIR.'class.usersession.php'); + require(INCLUDE_DIR.'class.signal.php'); + require(INCLUDE_DIR.'class.auth.php'); require(INCLUDE_DIR.'class.pagenate.php'); //Pagenate helper! require(INCLUDE_DIR.'class.log.php'); require(INCLUDE_DIR.'class.crypto.php'); diff --git a/client.inc.php b/client.inc.php index 7aeae56ee32283d4c8def33a1a8348ffb2b16404..ee9a9fcae443df2d4c0ade4636270d102b9955e8 100644 --- a/client.inc.php +++ b/client.inc.php @@ -43,11 +43,9 @@ require_once(INCLUDE_DIR.'class.dept.php'); //clear some vars $errors=array(); $msg=''; -$thisclient=$nav=null; +$nav=null; //Make sure the user is valid..before doing anything else. -if($_SESSION['_client']['userID'] && $_SESSION['_client']['key']) - $thisclient = new ClientSession($_SESSION['_client']['userID'],$_SESSION['_client']['key']); - +$thisclient = UserAuthenticationBackend::getUser(); //is the user logged in? if($thisclient && $thisclient->getId() && $thisclient->isValid()){ $thisclient->refreshSession(); diff --git a/include/ajax.content.php b/include/ajax.content.php index d4846823987fa3ac66f289cb1ff3dc57cb420310..acc238c34e828c414d3be133c836bb82d3f311b7 100644 --- a/include/ajax.content.php +++ b/include/ajax.content.php @@ -102,5 +102,28 @@ class ContentAjaxAPI extends AjaxController { return $content; } + + function getSignature($type, $id) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Login Required'); + + switch ($type) { + case 'none': + break; + case 'mine': + echo Format::viewableImages($thisstaff->getSignature()); + break; + case 'dept': + if (!($dept = Dept::lookup($id))) + Http::response(404, 'No such department'); + echo Format::viewableImages($dept->getSignature()); + break; + default: + Http::response(400, 'Unknown signature type'); + break; + } + } } ?> diff --git a/include/ajax.draft.php b/include/ajax.draft.php index 0caaeeb7bb497cbd06ca7a4e83b860f16232ec0b..3cad72194b839d4b4c26b114c0902c6201d9c727 100644 --- a/include/ajax.draft.php +++ b/include/ajax.draft.php @@ -107,7 +107,7 @@ class DraftAjaxAPI extends AjaxController { return Http::response(500, 'Unable to attach image'); echo JsonDataEncoder::encode(array( - 'content_id' => 'cid:'.$f->getHash(), + 'content_id' => 'cid:'.$f->getKey(), 'filelink' => sprintf('image.php?h=%s', $f->getDownloadHash()) )); } diff --git a/include/ajax.forms.php b/include/ajax.forms.php index cdd643a1b44f77d4e30c51bf3cd67fc5886acd77..2142e8e4faa947b5060539e79a8ef9d2b535025a 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -36,7 +36,5 @@ class DynamicFormsAjaxAPI extends AjaxController { else $field->save(); } - } - ?> diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 36795879261070b5e31ec14625866d59ba945f5c..248ee66d2210156f25ce120e18e603af91484c3e 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -31,11 +31,11 @@ class TicketsAjaxAPI extends AjaxController { $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $tickets=array(); - $sql='SELECT DISTINCT ticketID, email.address AS email' + $sql='SELECT DISTINCT `number`, email.address AS email' .' FROM '.TICKET_TABLE.' ticket' .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' - .' WHERE ticketID LIKE \''.db_input($_REQUEST['q'], false).'%\''; + .' WHERE `number` LIKE \''.db_input($_REQUEST['q'], false).'%\''; $sql.=' AND ( staff_id='.db_input($thisstaff->getId()); @@ -337,123 +337,192 @@ class TicketsAjaxAPI extends AjaxController { if(!$thisstaff || !($ticket=Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff)) Http::response(404, 'No such ticket'); - $staff=$ticket->getStaff(); - $lock=$ticket->getLock(); - $error=$msg=$warn=null; + ob_start(); + include STAFFINC_DIR . 'templates/ticket-preview.tmpl.php'; + $resp = ob_get_contents(); + ob_end_clean(); - if($lock && $lock->getStaffId()==$thisstaff->getId()) - $warn.=' <span class="Icon lockedTicket">Ticket is locked by '.$lock->getStaffName().'</span>'; - elseif($ticket->isOverdue()) - $warn.=' <span class="Icon overdueTicket">Marked overdue!</span>'; + return $resp; + } - ob_start(); - echo sprintf( - '<div style="width:500px; padding: 2px 2px 0 5px;"> - <h2>%s</h2><br>',Format::htmlchars($ticket->getSubject())); - - if($error) - echo sprintf('<div id="msg_error">%s</div>',$error); - elseif($msg) - echo sprintf('<div id="msg_notice">%s</div>',$msg); - elseif($warn) - echo sprintf('<div id="msg_warning">%s</div>',$warn); - - echo '<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; - - $ticket_state=sprintf('<span>%s</span>',ucfirst($ticket->getStatus())); - if($ticket->isOpen()) { - if($ticket->isOverdue()) - $ticket_state.=' — <span>Overdue</span>'; - else - $ticket_state.=sprintf(' — <span>%s</span>',$ticket->getPriority()); - } + function addRemoteCollaborator($tid, $bk, $id) { + global $thisstaff; - echo sprintf(' - <tr> - <th width="100">Ticket State:</th> - <td>%s</td> - </tr> - <tr> - <th>Create Date:</th> - <td>%s</td> - </tr>',$ticket_state, - Format::db_datetime($ticket->getCreateDate())); - if($ticket->isClosed()) { - echo sprintf(' - <tr> - <th>Close Date:</th> - <td>%s <span class="faded">by %s</span></td> - </tr>', - Format::db_datetime($ticket->getCloseDate()), - ($staff?$staff->getName():'staff') - ); - } elseif($ticket->getEstDueDate()) { - echo sprintf(' - <tr> - <th>Due Date:</th> - <td>%s</td> - </tr>', - Format::db_datetime($ticket->getEstDueDate())); - } - echo '</table>'; + if (!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'No such ticket'); + elseif (!$bk || !$id) + Http::response(422, 'Backend and user id required'); + elseif (!($backend = StaffAuthenticationBackend::getBackend($bk))) + Http::response(404, 'User not found'); + + $user_info = $backend->lookup($id); + $form = UserForm::getUserForm()->getForm($user_info); + $info = array(); + if (!$user_info) + $info['error'] = 'Unable to find user in directory'; + + return self::_addcollaborator($ticket, null, $form, $info); + } + + //Collaborators utils + function addCollaborator($tid, $uid=0) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'No such ticket'); - echo '<hr> - <table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; - if($ticket->isOpen()) { - echo sprintf(' - <tr> - <th width="100">Assigned To:</th> - <td>%s</td> - </tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">— Unassigned —</span>'); + $user = $uid? User::lookup($uid) : null; + + //If not a post then assume new collaborator form + if(!$_POST) + return self::_addcollaborator($ticket, $user); + + $user = $form = null; + if (isset($_POST['id']) && $_POST['id']) { //Existing user/ + $user = User::lookup($_POST['id']); + } else { //We're creating a new user! + $form = UserForm::getUserForm()->getForm($_POST); + $user = User::fromForm($form); + } + + $errors = $info = array(); + if ($user) { + if ($user->getId() == $ticket->getOwnerId()) + $errors['err'] = sprintf('Ticket owner, %s, is a collaborator by default!', + $user->getName()); + elseif (($c=$ticket->addCollaborator($user, + array('isactive'=>1), $errors))) { + $note = Format::htmlchars(sprintf('%s <%s> added as a collaborator', + $c->getName(), $c->getEmail())); + $ticket->logNote('New Collaborator Added', $note, + $thisstaff, false); + $info = array('msg' => sprintf('%s added as a collaborator', + $c->getName())); + return self::_collaborators($ticket, $info); + } } - echo sprintf( - ' <tr> - <th width="100">Department:</th> - <td>%s</td> - </tr> - <tr> - <th>Help Topic:</th> - <td>%s</td> - </tr> - <tr> - <th>From:</th> - <td>%s <span class="faded">%s</span></td> - </tr>', - Format::htmlchars($ticket->getDeptName()), - Format::htmlchars($ticket->getHelpTopic()), - Format::htmlchars($ticket->getName()), - $ticket->getEmail()); - echo ' - </table>'; - - $options = array(); - $options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid"); - if($ticket->getNumNotes()) - $options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes"); - - if($ticket->isOpen()) - $options[]=array('action'=>'Reply','url'=>"tickets.php?id=$tid#reply"); - - if($thisstaff->canAssignTickets()) - $options[]=array('action'=>($ticket->isAssigned()?'Reassign':'Assign'),'url'=>"tickets.php?id=$tid#assign"); - - if($thisstaff->canTransferTickets()) - $options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer"); - - $options[]=array('action'=>'Post Note','url'=>"tickets.php?id=$tid#note"); - - if($thisstaff->canEditTickets()) - $options[]=array('action'=>'Edit Ticket','url'=>"tickets.php?id=$tid&a=edit"); - - if($options) { - echo '<ul class="tip_menu">'; - foreach($options as $option) - echo sprintf('<li><a href="%s">%s</a></li>',$option['url'],$option['action']); - echo '</ul>'; + + if($errors && $errors['err']) { + $info +=array('error' => $errors['err']); + } else { + $info +=array('error' =>'Unable to add collaborator - try again'); } - echo '</div>'; + return self::_addcollaborator($ticket, $user, $form, $info); + } + + function updateCollaborator($cid) { + global $thisstaff; + + if(!($c=Collaborator::lookup($cid)) + || !($user=$c->getUser()) + || !($ticket=$c->getTicket()) + || !$ticket->checkStaffAccess($thisstaff) + ) + Http::response(404, 'Unknown collaborator'); + + $errors = array(); + if(!$user->updateInfo($_POST, $errors)) + return self::_collaborator($c ,$user->getForms($_POST), $errors); + + $info = array('msg' => sprintf('%s updated successfully', + $c->getName())); + + return self::_collaborators($ticket, $info); + } + + function viewCollaborator($cid) { + global $thisstaff; + + if(!($collaborator=Collaborator::lookup($cid)) + || !($ticket=$collaborator->getTicket()) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'Unknown collaborator'); + + return self::_collaborator($collaborator); + } + + function showCollaborators($tid) { + global $thisstaff; + + if(!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'No such ticket'); + + if($ticket->getCollaborators()) + return self::_collaborators($ticket); + + return self::_addcollaborator($ticket); + } + + function previewCollaborators($tid) { + global $thisstaff; + + if (!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'No such ticket'); + + ob_start(); + include STAFFINC_DIR . 'templates/collaborators-preview.tmpl.php'; + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function _addcollaborator($ticket, $user=null, $form=null, $info=array()) { + + $info += array( + 'title' => sprintf('Ticket #%s: Add a collaborator', $ticket->getNumber()), + 'action' => sprintf('#tickets/%d/add-collaborator', $ticket->getId()), + 'onselect' => sprintf('ajax.php/tickets/%d/add-collaborator/', $ticket->getId()), + ); + return self::_userlookup($user, $form, $info); + } + + + function updateCollaborators($tid) { + global $thisstaff; + + if(!($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'No such ticket'); + + $errors = $info = array(); + if ($ticket->updateCollaborators($_POST, $errors)) + Http::response(201, sprintf('Recipients (%d of %d)', + $ticket->getNumActiveCollaborators(), + $ticket->getNumCollaborators())); + + if($errors && $errors['err']) + $info +=array('error' => $errors['err']); + + return self::_collaborators($ticket, $info); + } + + + + function _collaborator($collaborator, $form=null, $info=array()) { + + $info += array('action' => '#collaborators/'.$collaborator->getId()); + + $user = $collaborator->getUser(); + + ob_start(); + include(STAFFINC_DIR . 'templates/user.tmpl.php'); + $resp = ob_get_contents(); + ob_end_clean(); + + return $resp; + } + + function _collaborators($ticket, $info=array()) { + + ob_start(); + include(STAFFINC_DIR . 'templates/collaborators.tmpl.php'); $resp = ob_get_contents(); ob_end_clean(); @@ -469,7 +538,7 @@ class TicketsAjaxAPI extends AjaxController { Http::response(404, 'No such ticket'); - if(!($user = $ticket->getOwner())) + if(!($user = User::lookup($ticket->getOwnerId()))) Http::response(404, 'Unknown user'); @@ -493,7 +562,7 @@ class TicketsAjaxAPI extends AjaxController { if(!$thisstaff || !($ticket=Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff) - || ! ($user = $ticket->getOwner())) + || !($user = User::lookup($ticket->getOwnerId()))) Http::response(404, 'No such ticket/user'); $errors = array(); @@ -523,12 +592,17 @@ class TicketsAjaxAPI extends AjaxController { Http::response(404, 'No such ticket'); - $user = $ticket->getOwner(); + $user = User::lookup($ticket->getOwnerId()); $info = array( 'title' => sprintf('Change user for ticket #%s', $ticket->getNumber()) ); + return self::_userlookup($user, null, $info); + } + + function _userlookup($user, $form, $info) { + ob_start(); include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php'); $resp = ob_get_contents(); @@ -537,6 +611,5 @@ class TicketsAjaxAPI extends AjaxController { } - } ?> diff --git a/include/ajax.tips.php b/include/ajax.tips.php index ed560403e945933600ad3390dea1aa508530ea8b..e81d1301b53622478520050bcd6e75528b28e36e 100644 --- a/include/ajax.tips.php +++ b/include/ajax.tips.php @@ -20,8 +20,13 @@ if(!defined('INCLUDE_DIR')) die('!'); require_once(INCLUDE_DIR.'class.i18n.php'); class HelpTipAjaxAPI extends AjaxController { - function getTipsJson($namespace, $lang='en_US') { - global $ost; + function getTipsJson($namespace, $lang=false) { + global $ost, $thisstaff; + + if (!$lang) + $lang = ($thisstaff) + ? $thisstaff->getLanguage() + : Internationalization::getDefaultLanguage(); $i18n = new Internationalization($lang); $tips = $i18n->getTemplate("help/tips/$namespace.yaml"); diff --git a/include/ajax.users.php b/include/ajax.users.php index c98c1b94d966ed82ffd1b4511f66aadcb8087f0c..2e5a83e32dfda68542fe3186ef66734a9c40b25b 100644 --- a/include/ajax.users.php +++ b/include/ajax.users.php @@ -30,6 +30,17 @@ class UsersAjaxAPI extends AjaxController { $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $users=array(); + $emails=array(); + foreach (StaffAuthenticationBackend::searchUsers($_REQUEST['q']) as $u) { + $name = "{$u['first']} {$u['last']}"; + $users[] = array('email' => $u['email'], 'name'=>$name, + 'info' => "{$u['email']} - $name (remote)", + 'id' => "auth:".$u['id'], "/bin/true" => $_REQUEST['q']); + $emails[] = $u['email']; + } + $remote_emails = ($emails = array_filter($emails)) + ? ' OR email.address IN ('.implode(',',db_input($emails)).') ' + : ''; $escaped = db_input(strtolower($_REQUEST['q']), false); $sql='SELECT DISTINCT user.id, email.address, name ' @@ -39,19 +50,25 @@ class UsersAjaxAPI extends AjaxController { LEFT JOIN '.FORM_ANSWER_TABLE.' value ON (value.entry_id=entry.id) ' .' WHERE email.address LIKE \'%'.$escaped.'%\' OR user.name LIKE \'%'.$escaped.'%\' - OR value.value LIKE \'%'.$escaped.'%\' - ORDER BY user.created ' + OR value.value LIKE \'%'.$escaped.'%\''.$remote_emails + .' ORDER BY user.created ' .' LIMIT '.$limit; if(($res=db_query($sql)) && db_num_rows($res)){ while(list($id,$email,$name)=db_fetch_row($res)) { + foreach ($users as $i=>$u) { + if ($u['email'] == $email) { + unset($users[$i]); + break; + } + } $name = Format::htmlchars($name); $users[] = array('email'=>$email, 'name'=>$name, 'info'=>"$email - $name", "id" => $id, "/bin/true" => $_REQUEST['q']); } } - return $this->json_encode($users); + return $this->json_encode(array_values($users)); } @@ -99,27 +116,34 @@ class UsersAjaxAPI extends AjaxController { function addUser() { - $valid = true; $form = UserForm::getUserForm()->getForm($_POST); - if (!$form->isValid()) - $valid = false; - - if (($field=$form->getField('email')) - && $field->getClean() - && User::lookup(array('emails__address'=>$field->getClean()))) { - $field->addError('Email is assigned to another user'); - $valid = false; - } - - if ($valid && ($user = User::fromForm($form->getClean()))) + if (($user = User::fromForm($form))) Http::response(201, $user->to_json()); - $info = array('error' =>'Error adding user - try again!'); return self::_lookupform($form, $info); } + function addRemoteUser($bk, $id) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Login Required'); + elseif (!$bk || !$id) + Http::response(422, 'Backend and user id required'); + elseif (!($backend = StaffAuthenticationBackend::getBackend($bk))) + Http::response(404, 'User not found'); + + $user_info = $backend->lookup($id); + $form = UserForm::getUserForm()->getForm($user_info); + $info = array('title' => 'Import Remote User'); + if (!$user_info) + $info['error'] = 'Unable to find user in directory'; + + include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php'); + } + function getLookupForm() { return self::_lookupform(); } @@ -151,5 +175,26 @@ class UsersAjaxAPI extends AjaxController { return $resp; } + function searchStaff() { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Login required for searching'); + elseif (!$thisstaff->isAdmin()) + Http::response(403, + 'Administrative privilege is required for searching'); + elseif (!isset($_REQUEST['q'])) + Http::response(400, 'Query argument is required'); + + $users = array(); + foreach (StaffAuthenticationBackend::allRegistered() as $ab) { + if (!$ab instanceof AuthDirectorySearch) + continue; + + foreach ($ab->search($_REQUEST['q']) as $u) + $users[] = $u; + } + return $this->json_encode($users); + } } ?> diff --git a/include/api.tickets.php b/include/api.tickets.php index 7cebb81be017d946b53e01d1be46a0d48b038905..80cdd1f10d873d2c7b16da863ceb9e0b5a1030d9 100644 --- a/include/api.tickets.php +++ b/include/api.tickets.php @@ -38,8 +38,11 @@ class TicketApiController extends ApiController { if(!strcasecmp($format, 'email')) { $supported = array_merge($supported, array('header', 'mid', - 'emailId', 'ticketId', 'reply-to', 'reply-to-name', - 'in-reply-to', 'references', 'thread-type')); + 'emailId', 'to-email-id', 'ticketId', 'reply-to', 'reply-to-name', + 'in-reply-to', 'references', 'thread-type', + 'recipients' => array('*' => array('name', 'email', 'source')) + )); + $supported['attachments']['*'][] = 'cid'; } @@ -103,7 +106,7 @@ class TicketApiController extends ApiController { if(!$ticket) return $this->exerr(500, "Unable to create new ticket: unknown error"); - $this->response(201, $ticket->getExtId()); + $this->response(201, $ticket->getNumber()); } /* private helper functions */ diff --git a/include/class.api.php b/include/class.api.php index b3e4ea7b349805b8d0489c23dabf0b22b1af7f77..65f0a90e89abaf42ecd5fa980d0b09299b738f3a 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -337,20 +337,12 @@ class ApiXmlDataParser extends XmlDataParser { } if (isset($value['encoding'])) $value['body'] = Format::utf8encode($value['body'], $value['encoding']); - // HTML-ize text if html is enabled - if ($cfg->isHtmlThreadEnabled() - && (!isset($value['type']) - || strcasecmp($value['type'], 'text/html'))) - $value = sprintf('<pre>%s</pre>', - Format::htmlchars($value['body'])); - // Text-ify html if html is disabled - elseif (!$cfg->isHtmlThreadEnabled() - && !strcasecmp($value['type'], 'text/html')) - $value = Format::html2text(Format::safe_html( - $value['body']), 100, false); - // Noop if they content-type matches the html setting + + if (!strcasecmp($value['type'], 'text/html')) + $value = new HtmlThreadBody($value['body']); else - $value = $value['body']; + $value = new TextThreadBody($value['body']); + } else if ($key == "attachments") { if(!isset($value['file'][':text'])) $value = $value['file']; @@ -390,11 +382,12 @@ class ApiJsonDataParser extends JsonDataParser { } elseif ($key == "message") { // Allow message specified in RFC 2397 format $data = Format::parseRfc2397($value, 'utf-8'); - if (!isset($data['type']) || $data['type'] != 'text/html') - $value = sprintf('<pre>%s</pre>', - Format::htmlchars($data['data'])); + + if (isset($data['type']) && $data['type'] == 'text/html') + $value = new HtmlThreadBody($data['data']); else - $value = $data['data']; + $value = new TextThreadBody($data['data']); + } else if ($key == "attachments") { foreach ($value as &$info) { $data = reset($info); @@ -408,10 +401,8 @@ class ApiJsonDataParser extends JsonDataParser { } unset($info); } - if (is_array($value)) { - $value = $this->fixup($value); - } } + unset($value); return $current; } @@ -432,9 +423,6 @@ class ApiEmailDataParser extends EmailDataParser { $data['source'] = 'Email'; - if(!$data['message']) - $data['message'] = '--'; - if(!$data['subject']) $data['subject'] = '[No Subject]'; diff --git a/include/class.attachment.php b/include/class.attachment.php index a46028d83c085241461771a0b3faaf091777273d..09d9826fd0444748830e63b18e5caf9bb5441f54 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -86,7 +86,7 @@ class Attachment { function getIdByFileHash($hash, $tid=0) { $sql='SELECT attach_id FROM '.TICKET_ATTACHMENT_TABLE.' a ' .' INNER JOIN '.FILE_TABLE.' f ON(f.id=a.file_id) ' - .' WHERE f.hash='.db_input($hash); + .' WHERE f.`key`='.db_input($hash); if($tid) $sql.=' AND a.ticket_id='.db_input($tid); @@ -128,7 +128,10 @@ class GenericAttachments { .',object_id='.db_input($this->getId()) .',file_id='.db_input($fileId) .',inline='.db_input($inline ? 1 : 0); - if (db_query($sql)) + // File may already be associated with the draft (in the + // event it was deleted and re-added) + if (db_query($sql, function($errno) { return $errno != 1062; }) + || db_errno() == 1062) $i[] = $fileId; } } @@ -157,7 +160,7 @@ class GenericAttachments { function _getList($separate=false, $inlines=false) { if(!isset($this->attachments)) { $this->attachments = array(); - $sql='SELECT f.id, f.size, f.hash, f.name, a.inline ' + $sql='SELECT f.id, f.size, f.`key`, f.name, a.inline ' .' FROM '.FILE_TABLE.' f ' .' INNER JOIN '.ATTACHMENT_TABLE.' a ON(f.id=a.file_id) ' .' WHERE a.`type`='.db_input($this->getType()) @@ -171,7 +174,6 @@ class GenericAttachments { $attachments = array(); foreach ($this->attachments as $a) { if ($a['inline'] != $separate || $a['inline'] == $inlines) { - $a['key'] = md5($a['id'].session_id().$a['hash']); $a['file_id'] = $a['id']; $attachments[] = $a; } diff --git a/include/class.auth.php b/include/class.auth.php new file mode 100644 index 0000000000000000000000000000000000000000..660acf9ba2aba5946acd40fd2ec07bedea154237 --- /dev/null +++ b/include/class.auth.php @@ -0,0 +1,834 @@ +<?php +require(INCLUDE_DIR.'class.ostsession.php'); +require(INCLUDE_DIR.'class.usersession.php'); + + +abstract class AuthenticatedUser { + //Authorization key returned by the backend used to authorize the user + private $authkey; + + // Get basic information + abstract function getId(); + abstract function getUsername(); + abstract function getRole(); + + //Backend used to authenticate the user + abstract function getAuthBackend(); + + //Authentication key + function setAuthKey($key) { + $this->authkey = $key; + } + + function getAuthKey() { + return $this->authkey; + } + + // logOut the user + function logOut() { + + if ($bk = $this->getAuthBackend()) + return $bk->signOut($this); + + return false; + } +} + +interface AuthDirectorySearch { + /** + * Indicates if the backend can be used to search for user information. + * Lookup is performed to find user information based on a unique + * identifier. + */ + function lookup($id); + + /** + * Indicates if the backend supports searching for usernames. This is + * distinct from information lookup in that lookup is intended to lookup + * information based on a unique identifier + */ + function search($query); +} + +/** + * Authentication backend + * + * Authentication provides the basis of abstracting the link between the + * login page with a username and password and the staff member, + * administrator, or client using the system. + * + * The system works by allowing the AUTH_BACKENDS setting from + * ost-config.php to determine the list of authentication backends or + * providers and also specify the order they should be evaluated in. + * + * The authentication backend should define a authenticate() method which + * receives a username and optional password. If the authentication + * succeeds, an instance deriving from <User> should be returned. + */ +abstract class AuthenticationBackend { + static protected $registry = array(); + static $name; + static $id; + + + /* static */ + static function register($class) { + if (is_string($class) && class_exists($class)) + $class = new $class(); + + if (!is_object($class) + || !($class instanceof AuthenticationBackend)) + return false; + + return static::_register($class); + } + + static function _register($class) { + // XXX: Raise error if $class::id is already in the registry + static::$registry[$class::$id] = $class; + } + + static function allRegistered() { + return static::$registry; + } + + static function getBackend($id) { + + if ($id + && ($backends = static::allRegistered()) + && isset($backends[$id])) + return $backends[$id]; + } + + /* + * Allow the backend to do login audit depending on the result + * This is mainly used to track failed login attempts + */ + static function authAudit($result, $credentials=null) { + + if (!$result) return; + + foreach (static::allRegistered() as $bk) + $bk->audit($result, $credentials); + } + + static function process($username, $password=null, &$errors) { + + if (!$username) + return false; + + $backends = static::getAllowedBackends($username); + foreach (static::allRegistered() as $bk) { + if ($backends //Allowed backends + && $bk->supportsAuthentication() + && !in_array($bk::$id, $backends)) + // User cannot be authenticated against this backend + continue; + + // All backends are queried here, even if they don't support + // authentication so that extensions like lockouts and audits + // can be supported. + $result = $bk->authenticate($username, $password); + if ($result instanceof AuthenticatedUser + && ($bk->login($result, $bk))) + return $result; + elseif ($result instanceof AccessDenied) { + break; + } + } + + if (!$result) + $result = new AccessDenied('Access denied'); + + if ($result && $result instanceof AccessDenied) + $errors['err'] = $result->reason; + + $info = array('username' => $username, 'password' => $password); + Signal::send('auth.login.failed', null, $info); + self::authAudit($result, $info); + } + + /* + * Attempt to process non-interactive sign-on e.g HTTP-Passthrough + * + * $forcedAuth - indicate if authentication is required. + * + */ + function processSignOn(&$errors, $forcedAuth=true) { + + foreach (static::allRegistered() as $bk) { + // All backends are queried here, even if they don't support + // authentication so that extensions like lockouts and audits + // can be supported. + $result = $bk->signOn(); + if ($result instanceof AuthenticatedUser) { + //Perform further Object specific checks and the actual login + if (!$bk->login($result, $bk)) + continue; + + return $result; + } + elseif ($result instanceof AccessDenied) { + break; + } + } + + if (!$result && $forcedAuth) + $result = new AccessDenied('Unknown user'); + + if ($result && $result instanceof AccessDenied) + $errors['err'] = $result->reason; + + self::authAudit($result); + } + + static function searchUsers($query) { + $users = array(); + foreach (static::allRegistered() as $bk) { + if ($bk instanceof AuthDirectorySearch) { + $users += $bk->search($query); + } + } + return $users; + } + + /** + * Fetches the friendly name of the backend + */ + function getName() { + return static::$name; + } + + /** + * Indicates if the backed supports authentication. Useful if the + * backend is used for logging or lockout only + */ + function supportsAuthentication() { + return true; + } + + /** + * Indicates if the backend supports changing a user's password. This + * would be done in two fashions. Either the currently-logged in user + * want to change its own password or a user requests to have their + * password reset. This requires an administrative privilege which this + * backend might not possess, so it's defined in supportsPasswordReset() + */ + function supportsPasswordChange() { + return false; + } + + function supportsPasswordReset() { + return false; + } + + function signOn() { + return null; + } + + protected function validate($auth) { + return null; + } + + protected function audit($result, $credentials) { + return null; + } + + abstract function authenticate($username, $password); + abstract function login($user, $bk); + abstract static function getUser(); //Validates authenticated users. + abstract function getAllowedBackends($userid); + abstract protected function getAuthKey($user); + abstract static function signOut($user); +} + +class RemoteAuthenticationBackend { + var $create_unknown_user = false; +} + +abstract class StaffAuthenticationBackend extends AuthenticationBackend { + + static private $_registry = array(); + + static function _register($class) { + static::$_registry[$class::$id] = $class; + } + + static function allRegistered() { + return array_merge(self::$_registry, parent::allRegistered()); + } + + function isBackendAllowed($staff, $bk) { + + if (!($backends=self::getAllowedBackends($staff->getId()))) + return true; //No restrictions + + return in_array($bk::$id, array_map('strtolower', $backends)); + } + + function getAllowedBackends($userid) { + + $backends =array(); + //XXX: Only one backend can be specified at the moment. + $sql = 'SELECT backend FROM '.STAFF_TABLE + .' WHERE backend IS NOT NULL '; + if (is_numeric($userid)) + $sql.= ' AND staff_id='.db_input($userid); + else { + $sql.= ' AND (username='.db_input($userid) .' OR email='.db_input($userid).')'; + } + + if (($res=db_query($sql, false)) && db_num_rows($res)) + $backends[] = db_result($res); + + return array_filter($backends); + } + + function login($staff, $bk) { + global $ost; + + if (!$bk || !($staff instanceof Staff)) + return false; + + // Ensure staff is allowed for realz to be authenticated via the backend. + if (!static::isBackendAllowed($staff, $bk) + || !($authkey=$bk->getAuthKey($staff))) + return false; + + //Log debug info. + $ost->logDebug('Staff login', + sprintf("%s logged in [%s], via %s", $staff->getUserName(), + $_SERVER['REMOTE_ADDR'], get_class($bk))); //Debug. + + $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() ' + .' WHERE staff_id='.db_input($staff->getId()); + db_query($sql); + + //Tag the authkey. + $authkey = $bk::$id.':'.$authkey; + + //Now set session crap and lets roll baby! + $authsession = &$_SESSION['_auth']['staff']; + + $authsession = array(); //clear. + $authsession['id'] = $staff->getId(); + $authsession['key'] = $authkey; + + $staff->setAuthKey($authkey); + $staff->refreshSession(true); //set the hash. + + $_SESSION['TZ_OFFSET'] = $staff->getTZoffset(); + $_SESSION['TZ_DST'] = $staff->observeDaylight(); + + Signal::send('auth.login.succeeded', $staff); + + return true; + } + + /* Base signOut + * + * Backend should extend the signout and perform any additional signout + * it requires. + */ + + static function signOut($staff) { + global $ost; + + $_SESSION['_auth']['staff'] = array(); + unset($_SESSION[':token']['staff']); + $ost->logDebug('Staff logout', + sprintf("%s logged out [%s]", + $staff->getUserName(), + $_SERVER['REMOTE_ADDR'])); //Debug. + + Signal::send('auth.logout', $staff); + } + + // Called to get authenticated user (if any) + static function getUser() { + + if (!isset($_SESSION['_auth']['staff']) + || !$_SESSION['_auth']['staff']['key']) + return null; + + list($id, $auth) = explode(':', $_SESSION['_auth']['staff']['key']); + + if (!($bk=static::getBackend($id)) //get the backend + || !($staff = $bk->validate($auth)) //Get AuthicatedUser + || !($staff instanceof Staff) + || $staff->getId() != $_SESSION['_auth']['staff']['id'] // check ID + ) + return null; + + $staff->setAuthKey($_SESSION['_auth']['staff']['key']); + + return $staff; + } + + function authenticate($username, $password) { + return false; + } + + // Generic authentication key for staff's backend is the username + protected function getAuthKey($staff) { + + if(!($staff instanceof Staff)) + return null; + + return $staff->getUsername(); + } + + protected function validate($authkey) { + + if (($staff = new StaffSession($authkey)) && $staff->getId()) + return $staff; + } +} + +abstract class UserAuthenticationBackend extends AuthenticationBackend { + + static private $_registry = array(); + + static function _register($class) { + static::$_registry[$class::$id] = $class; + } + + static function allRegistered() { + return array_merge(self::$_registry, parent::allRegistered()); + } + + function getAllowedBackends($userid) { + // White listing backends for specific user not supported. + return array(); + } + + function login($user, $bk) { + global $ost; + + if (!$user || !$bk + || !$bk::$id //Must have ID + || !($authkey = $bk->getAuthKey($user))) + return false; + + //Tag the authkey. + $authkey = $bk::$id.':'.$authkey; + + //Set the session goodies + $authsession = &$_SESSION['_auth']['user']; + + $authsession = array(); //clear. + $authsession['id'] = $user->getId(); + $authsession['key'] = $authkey; + $_SESSION['TZ_OFFSET'] = $ost->getConfig()->getTZoffset(); + $_SESSION['TZ_DST'] = $ost->getConfig()->observeDaylightSaving(); + + //The backend used decides the format of the auth key. + // XXX: encrypt to hide the bk?? + $user->setAuthKey($authkey); + + $user->refreshSession(true); //set the hash. + + //Log login info... + $msg=sprintf('%s (%s) logged in [%s]', + $user->getUserName(), $user->getId(), $_SERVER['REMOTE_ADDR']); + $ost->logDebug('User login', $msg); + + return true; + } + + function authenticate($username, $password) { + return false; + } + + static function signOut($user) { + global $ost; + + $_SESSION['_auth']['user'] = array(); + unset($_SESSION[':token']['client']); + $ost->logDebug('User logout', + sprintf("%s logged out [%s]", + $user->getUserName(), $_SERVER['REMOTE_ADDR'])); + } + + protected function getAuthKey($user) { + return $user->getUsername(); + } + + static function getUser() { + + if (!isset($_SESSION['_auth']['user']) + || !$_SESSION['_auth']['user']['key']) + return null; + + list($id, $auth) = explode(':', $_SESSION['_auth']['user']['key']); + + if (!($bk=static::getBackend($id)) //get the backend + || !$bk->supportsAuthentication() //Make sure it can authenticate + || !($user=$bk->validate($auth)) //Get AuthicatedUser + || !($user instanceof AuthenticatedUser) // Make sure it user + || $user->getId() != $_SESSION['_auth']['user']['id'] // check ID + ) + return null; + + $user->setAuthKey($_SESSION['_auth']['user']['key']); + + return $user; + } +} + +/** + * This will be an exception in later versions of PHP + */ +class AccessDenied { + function __construct($reason) { + $this->reason = $reason; + } +} + +/** + * Simple authentication backend which will lock the login form after a + * configurable number of attempts + */ +abstract class AuthStrikeBackend extends AuthenticationBackend { + + function authenticate($username, $password=null) { + return static::authTimeout(); + } + + function signOn() { + return static::authTimeout(); + } + + static function signOut($user) { + return false; + } + + + function login($user, $bk) { + return false; + } + + static function getUser() { + return null; + } + + function supportsAuthentication() { + return false; + } + + function getAllowedBackends($userid) { + return array(); + } + + function getAuthKey($user) { + return null; + } + + //Provides audit facility for logins attempts + function audit($result, $credentials) { + + //Count failed login attempts as a strike. + if ($result instanceof AccessDenied) + return static::authStrike($credentials); + + } + + abstract function authStrike($credentials); + abstract function authTimeout(); +} + +/* + * Backend to monitor staff's failed login attempts + */ +class StaffAuthStrikeBackend extends AuthStrikeBackend { + + function authTimeout() { + global $ost; + + $cfg = $ost->getConfig(); + + $authsession = &$_SESSION['_auth']['staff']; + if (!$authsession['laststrike']) + return; + + //Veto login due to excessive login attempts. + if((time()-$authsession['laststrike'])<$cfg->getStaffLoginTimeout()) { + $authsession['laststrike'] = time(); //reset timer. + return new AccessDenied('Max. failed login attempts reached'); + } + + //Timeout is over. + //Reset the counter for next round of attempts after the timeout. + $authsession['laststrike']=null; + $authsession['strikes']=0; + } + + function authstrike($credentials) { + global $ost; + + $cfg = $ost->getConfig(); + + $authsession = &$_SESSION['_auth']['staff']; + + $username = $credentials['username']; + + $authsession['strikes']+=1; + if($authsession['strikes']>$cfg->getStaffMaxLogins()) { + $authsession['laststrike']=time(); + $alert='Excessive login attempts by a staff member?'."\n". + 'Username: '.$username."\n" + .'IP: '.$_SERVER['REMOTE_ADDR']."\n" + .'TIME: '.date('M j, Y, g:i a T')."\n\n" + .'Attempts #'.$authsession['strikes']."\n" + .'Timeout: '.($cfg->getStaffLoginTimeout()/60)." minutes \n\n"; + $ost->logWarning('Excessive login attempts ('.$username.')', $alert, + $cfg->alertONLoginError()); + return new AccessDenied('Forgot your login info? Contact Admin.'); + //Log every other third failed login attempt as a warning. + } elseif($authsession['strikes']%3==0) { + $alert='Username: '.$username."\n" + .'IP: '.$_SERVER['REMOTE_ADDR']."\n" + .'TIME: '.date('M j, Y, g:i a T')."\n\n" + .'Attempts #'.$authsession['strikes']; + $ost->logWarning('Failed staff login attempt ('.$username.')', $alert, false); + } + } +} +StaffAuthenticationBackend::register('StaffAuthStrikeBackend'); + +/* + * Backend to monitor user's failed login attempts + */ +class UserAuthStrikeBackend extends AuthStrikeBackend { + + function authTimeout() { + global $ost; + + $cfg = $ost->getConfig(); + + $authsession = &$_SESSION['_auth']['user']; + if (!$authsession['laststrike']) + return; + + //Veto login due to excessive login attempts. + if ((time()-$authsession['laststrike']) < $cfg->getStaffLoginTimeout()) { + $authsession['laststrike'] = time(); //reset timer. + return new AccessDenied("You've reached maximum failed login attempts allowed."); + } + + //Timeout is over. + //Reset the counter for next round of attempts after the timeout. + $authsession['laststrike']=null; + $authsession['strikes']=0; + } + + function authstrike($credentials) { + global $ost; + + $cfg = $ost->getConfig(); + + $authsession = &$_SESSION['_auth']['user']; + + $username = $credentials['username']; + $password = $credentials['password']; + + $authsession['strikes']+=1; + if($authsession['strikes']>$cfg->getClientMaxLogins()) { + $authsession['laststrike'] = time(); + $alert='Excessive login attempts by a user.'."\n". + 'Login: '.$username.': '.$password."\n". + 'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n". + 'Attempts #'.$authsession['strikes']; + $ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError())); + return new AccessDenied('Access Denied'); + } elseif($authsession['strikes']%3==0) { //Log every other third failed login attempt as a warning. + $alert='Login: '.$username.': '.$password."\n".'IP: '.$_SERVER['REMOTE_ADDR']. + "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$authsession['strikes']; + $ost->logWarning('Failed login attempt (user)', $alert); + } + + } +} +UserAuthenticationBackend::register('UserAuthStrikeBackend'); + + +class osTicketAuthentication extends StaffAuthenticationBackend { + static $name = "Local Authentication"; + static $id = "local"; + + function authenticate($username, $password) { + if (($user = new StaffSession($username)) && $user->getId() && + $user->check_passwd($password)) { + + //update last login && password reset stuff. + $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() '; + if($user->isPasswdResetDue() && !$user->isAdmin()) + $sql.=',change_passwd=1'; + $sql.=' WHERE staff_id='.db_input($user->getId()); + db_query($sql); + + return $user; + } + } + +} +StaffAuthenticationBackend::register('osTicketAuthentication'); + +class PasswordResetTokenBackend extends StaffAuthenticationBackend { + static $id = "pwreset.staff"; + + function supportsAuthentication() { + return false; + } + + function signOn($errors=array()) { + global $ost; + + if (!isset($_POST['userid']) || !isset($_POST['token'])) + return false; + elseif (!($_config = new Config('pwreset'))) + return false; + elseif (($staff = new StaffSession($_POST['userid'])) && + !$staff->getId()) + $errors['msg'] = 'Invalid user-id given'; + elseif (!($id = $_config->get($_POST['token'])) + || $id != $staff->getId()) + $errors['msg'] = 'Invalid reset token'; + elseif (!($ts = $_config->lastModified($_POST['token'])) + && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts)))) + $errors['msg'] = 'Invalid reset token'; + elseif (!$staff->forcePasswdRest()) + $errors['msg'] = 'Unable to reset password'; + else + return $staff; + } + + function login($staff, $bk) { + $_SESSION['_staff']['reset-token'] = $_POST['token']; + Signal::send('auth.pwreset.login', $staff); + return parent::login($staff, $bk); + } +} +StaffAuthenticationBackend::register('PasswordResetTokenBackend'); + +/* + * AuthToken Authentication Backend + * + * Provides auto-login facility for end users with valid link + * + * Ticket used to loggin is tracked durring the session this is + * important in the future when auto-logins will be + * limited to single ticket view. + */ +class AuthTokenAuthentication extends UserAuthenticationBackend { + static $name = "Auth Token Authentication"; + static $id = "authtoken"; + + + function signOn() { + + $user = null; + if ($_GET['auth']) { + if (($u = TicketUser::lookupByToken($_GET['auth']))) + $user = new ClientSession($u); + } + // Support old ticket based tokens. + elseif ($_GET['t'] && $_GET['e'] && $_GET['a']) { + if (($ticket = Ticket::lookupByNumber($_GET['t'], $_GET['e'])) + // Using old ticket auth code algo - hardcoded here because it + // will be removed in ticket class in the upcoming rewrite + && !strcasecmp($_GET['a'], md5($ticket->getId() . $_GET['e'] . SECRET_SALT)) + && ($owner = $ticket->getOwner())) + $user = new ClientSession($owner); + } + + return $user; + } + + + protected function getAuthKey($user) { + + if (!$this->supportsAuthentication() || !$user) + return null; + + //Generate authkey based the type of ticket user + // It's required to validate users going forward. + $authkey = sprintf('%s%dt%dh%s', //XXX: Placeholder + ($user->isOwner() ? 'o':'c'), + $user->getId(), + $user->getTicketId(), + md5($user->getId().$this->id)); + + return $authkey; + } + + protected function validate($authkey) { + + $regex = '/^(?P<type>\w{1})(?P<id>\d+)t(?P<tid>\d+)h(?P<hash>.*)$/i'; + $matches = array(); + if (!preg_match($regex, $authkey, $matches)) + return false; + + $user = null; + switch ($matches['type']) { + case 'c': //Collaborator + $criteria = array( 'userId' => $matches['id'], + 'ticketId' => $matches['tid']); + if (($c = Collaborator::lookup($criteria)) + && ($c->getTicketId() == $matches['tid'])) + $user = new ClientSession($c); + break; + case 'o': //Ticket owner + if (($ticket = Ticket::lookup($matches['tid'])) + && ($o = $ticket->getOwner()) + && ($o->getId() == $matches['id'])) + $user = new ClientSession($o); + break; + } + + //Make sure the authkey matches. + if (!$user || strcmp($this->getAuthKey($user), $authkey)) + return null; + + + return $user; + } + +} +UserAuthenticationBackend::register('AuthTokenAuthentication'); + +//Simple ticket lookup backend used to recover ticket access link. +// We're using authentication backend so we can guard aganist brute force +// attempts (which doesn't buy much since the link is emailed) +class AccessLinkAuthentication extends UserAuthenticationBackend { + static $name = "Ticket Access Link Authentication"; + static $id = "authlink"; + + function authenticate($email, $number) { + + if (!($ticket = Ticket::lookupByNumber($number)) + || !($user=User::lookup(array('emails__address' => + $email)))) + return false; + + //Ticket owner? + if ($ticket->getUserId() == $user->getId()) + $user = $ticket->getOwner(); + //Collaborator? + elseif (!($user = Collaborator::lookup(array('userId' => + $user->getId(), 'ticketId' => + $ticket->getId())))) + return false; //Bro, we don't know you! + + + return new ClientSession($user); + } + + //We are not actually logging in the user.... + function login($user, $bk) { + return true; + } + +} +UserAuthenticationBackend::register('AccessLinkAuthentication'); +?> diff --git a/include/class.base32.php b/include/class.base32.php new file mode 100755 index 0000000000000000000000000000000000000000..ce83828f873d208f72fb3cdce35ab990d6aab673 --- /dev/null +++ b/include/class.base32.php @@ -0,0 +1,121 @@ +<?php +/* + * Base32 encoder/decoder + * + * Jared Hancock <jared@osticket.com> + * Copyright (c) osTicket.com + */ + + +class Base32 { + + /** + * encode a binary string + * + * @param $inString Binary string to base32 encode + * @return $outString Base32 encoded $inString + * + * Original code from + * http://www.phpkode.com/source/p/moodle/moodle/lib/base32.php. Optimized + * to double performance + */ + + function encode($inString) + { + $outString = ""; + $compBits = ""; + static $BASE32_TABLE = array( + '00000' => 'a', '00001' => 'b', '00010' => 'c', '00011' => 'd', + '00100' => 'e', '00101' => 'f', '00110' => 'g', '00111' => 'h', + '01000' => 'i', '01001' => 'j', '01010' => 'k', '01011' => 'l', + '01100' => 'm', '01101' => 'n', '01110' => 'o', '01111' => 'p', + '10000' => 'q', '10001' => 'r', '10010' => 's', '10011' => 't', + '10100' => 'u', '10101' => 'v', '10110' => 'w', '10111' => 'x', + '11000' => 'y', '11001' => 'z', '11010' => '0', '11011' => '1', + '11100' => '2', '11101' => '3', '11110' => '4', '11111' => '5'); + + /* Turn the compressed string into a string that represents the bits as 0 and 1. */ + for ($i = 0, $k = strlen($inString); $i < $k; $i++) { + $compBits .= str_pad(decbin(ord($inString[$i])), 8, '0', STR_PAD_LEFT); + } + + /* Pad the value with enough 0's to make it a multiple of 5 */ + if ((($len = strlen($compBits)) % 5) != 0) { + $compBits = str_pad($compBits, $len+(5-($len % 5)), '0', STR_PAD_RIGHT); + } + + /* Create an array by chunking it every 5 chars */ + $fiveBitsArray = str_split($compBits, 5); + + /* Look-up each chunk and add it to $outstring */ + foreach ($fiveBitsArray as $fiveBitsString) { + $outString .= $BASE32_TABLE[$fiveBitsString]; + } + + return $outString; + } + + + + /** + * decode to a binary string + * + * @param $inString String to base32 decode + * + * @return $outString Base32 decoded $inString + * + * @access private + * + */ + + function decode($inString) { + /* declaration */ + $deCompBits = ''; + $outString = ''; + + static $BASE32_TABLE = array( + 'a' => '00000', 'b' => '00001', 'c' => '00010', 'd' => '00011', + 'e' => '00100', 'f' => '00101', 'g' => '00110', 'h' => '00111', + 'i' => '01000', 'j' => '01001', 'k' => '01010', 'l' => '01011', + 'm' => '01100', 'n' => '01101', 'o' => '01110', 'p' => '01111', + 'q' => '10000', 'r' => '10001', 's' => '10010', 't' => '10011', + 'u' => '10100', 'v' => '10101', 'w' => '10110', 'x' => '10111', + 'y' => '11000', 'z' => '11001', '0' => '11010', '1' => '11011', + '2' => '11100', '3' => '11101', '4' => '11110', '5' => '11111'); + + /* Step 1 */ + $inputCheck = strlen($inString) % 8; + if(($inputCheck == 1)||($inputCheck == 3)||($inputCheck == 6)) { + trigger_error('input to Base32Decode was a bad mod length: '.$inputCheck); + return false; + } + + /* $deCompBits is a string that represents the bits as 0 and 1.*/ + for ($i = 0, $k = strlen($inString); $i < $k; $i++) { + $inChar = $inString[$i]; + if(isset($BASE32_TABLE[$inChar])) { + $deCompBits .= $BASE32_TABLE[$inChar]; + } else { + trigger_error('input to Base32Decode had a bad character: '.$inChar); + return false; + } + } + + /* Break the decompressed string into octets for returning */ + foreach (str_split($deCompBits, 8) as $chunk) { + if (strlen($chunk) != 8) { + // Ensure correct padding + if (substr_count($chunk, '1')>0) { + trigger_error('found non-zero padding in Base32Decode'); + return false; + } + break; + } + $outString .= chr(bindec($chunk)); + } + + return $outString; + } +} + +?> diff --git a/include/class.client.php b/include/class.client.php index 078b21bfdcc78811263e2ce28b59dac4a61a469a..c9f6fc394ec8fa53619d2d1b2867dfdccbb3db3d 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -2,10 +2,7 @@ /********************************************************************* class.client.php - Handles everything about client - - XXX: Please note that osTicket uses email address and ticket ID to authenticate the user*! - Client is modeled on the info of the ticket used to login . + Handles everything about EndUser Peter Rotich <peter@osticket.com> Copyright (c) 2006-2013 osTicket @@ -16,112 +13,204 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ +abstract class TicketUser { + + static private $token_regex = '/^(?P<type>\w{1})(?P<algo>\d+)x(?P<hash>.*)$/i'; -class Client { + protected $user; - var $id; - var $fullname; - var $username; - var $email; + function __construct($user) { + $this->user = $user; + } - var $_answers; + function __call($name, $args) { + global $cfg; - var $ticket_id; - var $user_id; - var $ticketID; + $rv = null; + if($this->user && is_callable(array($this->user, $name))) + $rv = $args + ? call_user_func_array(array($this->user, $name), $args) + : call_user_func(array($this->user, $name)); - var $ht; + if ($rv) return $rv; + $tag = substr($name, 3); + switch (strtolower($tag)) { + case 'ticket_link': + return sprintf('%s/view.php?auth=%s', + $cfg->getBaseUrl(), + urlencode($this->getAuthToken())); + break; + } + + return false; - function Client($id, $email=null) { - $this->id =0; - $this->load($id,$email); } - function load($id=0, $email=null) { + function sendAccessLink() { + global $ost; - if(!$id && !($id=$this->getId())) - return false; + if (!($ticket = $this->getTicket()) + || !($dept = $ticket->getDept()) + || !($email = $dept->getAutoRespEmail()) + || !($tpl = $dept->getTemplate()->getMsgTemplate('user.accesslink'))) + return; - $sql='SELECT ticket.ticket_id, ticketID, email.address as email, user.id as user_id ' - .' FROM '.TICKET_TABLE.' ticket ' - .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' - .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' - .' WHERE ticketID='.db_input($id); + $vars = array( + 'url' => $ost->getConfig()->getBaseUrl(), + 'ticket' => $this->getTicket(), + 'recipient' => $this); - if($email) - $sql.=' AND email.address = '.db_input($email); + $msg = $ost->replaceTemplateVariables($tpl->asArray(), $vars); + $email->send($this->getEmail(), $msg['subj'], $msg['body']); + } - if(!($res=db_query($sql)) || !db_num_rows($res)) - return NULL; + protected function getAuthToken($algo=1) { - $this->ht = db_fetch_array($res); - $this->id = $this->ht['ticketID']; //placeholder - $this->ticket_id = $this->ht['ticket_id']; - $this->ticketID = $this->ht['ticketID']; + //Format: // <user type><algo id used>x<pack of uid & tid><hash of the algo> + $authtoken = sprintf('%s%dx%s', + ($this->isOwner() ? 'o' : 'c'), + $algo, + Base32::encode(pack('VV',$this->getId(), $this->getTicketId()))); - $user = User::lookup($this->ht['user_id']); - $this->user_id = $this->ht['user_id']; - $this->fullname = $user->getFullName(); + switch($algo) { + case 1: + $authtoken .= substr(base64_encode( + md5($this->getId().$this->getTicket()->getCreateDate().$this->getTicketId().SECRET_SALT, true)), 8); + break; + default: + return null; + } - $this->username = $this->ht['email']; - $this->email = $this->ht['email']; + return $authtoken; + } + + static function lookupByToken($token) { + + //Expecting well formatted token see getAuthToken routine for details. + $matches = array(); + if (!preg_match(static::$token_regex, $token, $matches)) + return null; + + //Unpack the user and ticket ids + $matches +=unpack('Vuid/Vtid', + Base32::decode(strtolower(substr($matches['hash'], 0, 13)))); + + $user = null; + switch ($matches['type']) { + case 'c': //Collaborator c + if (($user = Collaborator::lookup($matches['uid'])) + && $user->getTicketId() != $matches['tid']) + $user = null; + break; + case 'o': //Ticket owner + if (($ticket = Ticket::lookup($matches['tid']))) { + if (($user = $ticket->getOwner()) + && $user->getId() != $matches['uid']) + $user = null; + } + break; + } - $this->stats = array(); + if (!$user + || !$user instanceof TicketUser + || strcasecmp($user->getAuthToken($matches['algo']), $token)) + return false; - return($this->id); + return $user; } - function loadDynamicData() { - $this->_answers = array(); - foreach (DynamicFormEntry::forClient($this->getId()) as $form) - foreach ($form->getAnswers() as $answer) - $this->_answers[$answer->getField()->get('name')] = - $answer->getValue(); + static function lookupByEmail($email) { + + if (!($user=User::lookup(array('emails__address' => $email)))) + return null; + + return new EndUser($user); } - function reload() { - return $this->load(); + function isOwner() { + return ($this->user + && $this->user->getId() == $this->getTicket()->getOwnerId()); } - function isClient() { - return TRUE; + abstract function getTicketId(); + abstract function getTicket(); +} + +class TicketOwner extends TicketUser { + + protected $ticket; + + function __construct($user, $ticket) { + parent::__construct($user); + $this->ticket = $ticket; } - function getId() { - return $this->id; + function getTicket() { + return $this->ticket; } - function getUserId() { - return $this->user_id; + function getTicketId() { + return $this->ticket->getId(); } +} + +/* + * Decorator class for authenticated user + * + */ - function getEmail() { - return $this->email; +class EndUser extends AuthenticatedUser { + + protected $user; + + function __construct($user) { + $this->user = $user; } - function getUserName() { - return $this->username; + /* + * Delegate calls to the user + */ + function __call($name, $args) { + + if(!$this->user + || !is_callable(array($this->user, $name))) + return false; + + return $args + ? call_user_func_array(array($this->user, $name), $args) + : call_user_func(array($this->user, $name)); } - function getName() { - return $this->fullname; + function getId() { + //We ONLY care about user ID at the ticket level + if ($this->user instanceof Collaborator) + return $this->user->getUserId(); + + return $this->user->getId(); } - function getPhone() { - return $this->_answers['phone']; + function getUserName() { + //XXX: Revisit when real usernames are introduced or when email + // requirement is removed. + return $this->user->getEmail(); } - function getTicketID() { - return $this->ticketID; + function getRole() { + return $this->isOwner() ? 'owner' : 'collaborator'; + } + + function getAuthBackend() { + list($authkey,) = explode(':', $this->getAuthKey()); + return UserAuthenticationBackend::getBackend($authkey); } function getTicketStats() { - if(!$this->stats['tickets']) - $this->stats['tickets'] = Ticket::getClientStats($this->getEmail()); + if (!isset($this->ht['stats'])) + $this->ht['stats'] = $this->getStats(); - return $this->stats['tickets']; + return $this->ht['stats']; } function getNumTickets() { @@ -136,114 +225,21 @@ class Client { return ($stats=$this->getTicketStats())?$stats['closed']:0; } - /* ------------- Static ---------------*/ - function getLastTicketIdByEmail($email) { - $sql='SELECT ticket.ticketID FROM '.TICKET_TABLE.' ticket ' - .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' - .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' - .' WHERE email.address = '.db_input($email) - .' ORDER BY ticket.created ' - .' LIMIT 1'; - if(($res=db_query($sql)) && db_num_rows($res)) - list($tid) = db_fetch_row($res); - - return $tid; - } - - function lookup($id, $email=null) { - return ($id && is_numeric($id) && ($c=new Client($id,$email)) && $c->getId()==$id)?$c:null; - } + private function getStats() { - function lookupByEmail($email) { - return (($id=self::getLastTicketIdByEmail($email)))?self::lookup($id, $email):null; - } - - /* static */ function login($ticketID, $email, $auth=null, &$errors=array()) { - global $ost; - - - $cfg = $ost->getConfig(); - $auth = trim($auth); - $email = trim($email); - $ticketID = trim($ticketID); - - # Only consider auth token for GET requests, and for GET requests, - # REQUIRE the auth token - $auto_login = ($_SERVER['REQUEST_METHOD'] == 'GET'); - - //Check time for last max failed login attempt strike. - if($_SESSION['_client']['laststrike']) { - if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) { - $errors['login'] = 'Excessive failed login attempts'; - $errors['err'] = 'You\'ve reached maximum failed login attempts allowed. Try again later or <a href="open.php">open a new ticket</a>'; - $_SESSION['_client']['laststrike'] = time(); //renew the strike. - } else { //Timeout is over. - //Reset the counter for next round of attempts after the timeout. - $_SESSION['_client']['laststrike'] = null; - $_SESSION['_client']['strikes'] = 0; - } - } - - if($auto_login && !$auth) - $errors['login'] = 'Invalid method'; - elseif(!$ticketID || !Validator::is_email($email)) - $errors['login'] = 'Valid email and ticket number required'; - - //Bail out on error. - if($errors) return false; - - //See if we can fetch local ticket id associated with the ID given - if(($ticket=Ticket::lookupByExtId($ticketID, $email)) && $ticket->getId()) { - //At this point we know the ticket ID is valid. - //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? - //Check the email given. - - # Require auth token for automatic logins (GET METHOD). - if (!strcasecmp($ticket->getEmail(), $email) && (!$auto_login || $auth === $ticket->getAuthToken())) { - - //valid match...create session goodies for the client. - $user = new ClientSession($email,$ticket->getExtId()); - $_SESSION['_client'] = array(); //clear. - $_SESSION['_client']['userID'] = $ticket->getEmail(); //Email - $_SESSION['_client']['key'] = $ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. - $_SESSION['_client']['token'] = $user->getSessionToken(); - $_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); - $_SESSION['TZ_DST'] = $cfg->observeDaylightSaving(); - $user->refreshSession(true); //set the hash. - //Log login info... - $msg=sprintf('%s/%s logged in [%s]', $ticket->getEmail(), $ticket->getExtId(), $_SERVER['REMOTE_ADDR']); - $ost->logDebug('User login', $msg); - - //Regenerate session ID. - $sid=session_id(); //Current session id. - session_regenerate_id(TRUE); //get new ID. - if(($session=$ost->getSession()) && is_object($session) && $sid!=session_id()) - $session->destroy($sid); - - return $user; - - } - } - - //If we get to this point we know the login failed. - $errors['login'] = 'Invalid login'; - $_SESSION['_client']['strikes']+=1; - if(!$errors && $_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) { - $errors['login'] = 'Access Denied'; - $errors['err'] = 'Forgot your login info? Please <a href="open.php">open a new ticket</a>.'; - $_SESSION['_client']['laststrike'] = time(); - $alert='Excessive login attempts by a user.'."\n". - 'Email: '.$email."\n".'Ticket#: '.$ticketID."\n". - 'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n". - 'Attempts #'.$_SESSION['_client']['strikes']; - $ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError())); - } elseif($_SESSION['_client']['strikes']%2==0) { //Log every other failed login attempt as a warning. - $alert='Email: '.$email."\n".'Ticket #: '.$ticketID."\n".'IP: '.$_SERVER['REMOTE_ADDR']. - "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes']; - $ost->logWarning('Failed login attempt (user)', $alert); - } - - return false; + $sql='SELECT count(open.ticket_id) as open, count(closed.ticket_id) as closed ' + .' FROM '.TICKET_TABLE.' ticket ' + .' LEFT JOIN '.TICKET_TABLE.' open + ON (open.ticket_id=ticket.ticket_id AND open.status=\'open\') ' + .' LEFT JOIN '.TICKET_TABLE.' closed + ON (closed.ticket_id=ticket.ticket_id AND closed.status=\'closed\')' + .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab + ON (collab.ticket_id=ticket.ticket_id + AND collab.user_id = '.db_input($this->getId()).' )' + .' WHERE ticket.user_id = '.db_input($this->getId()) + .' OR collab.user_id = '.db_input($this->getId()); + + return db_fetch_array(db_query($sql)); } } ?> diff --git a/include/class.collaborator.php b/include/class.collaborator.php new file mode 100644 index 0000000000000000000000000000000000000000..b8adb93262d1dba483457b7decd82b8af7dd6078 --- /dev/null +++ b/include/class.collaborator.php @@ -0,0 +1,159 @@ +<?php +/********************************************************************* + class.collaborator.php + + Ticket collaborator + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require_once(INCLUDE_DIR . 'class.user.php'); +require_once(INCLUDE_DIR . 'class.client.php'); + +class Collaborator extends TicketUser { + + var $ht; + + var $user; + var $ticket; + + function __construct($id) { + $this->load($id); + parent::__construct($this->getUser()); + } + + function load($id) { + + if(!$id && !($id=$this->getId())) + return; + + $sql='SELECT * FROM '.TICKET_COLLABORATOR_TABLE + .' WHERE id='.db_input($id); + + $this->ht = db_fetch_array(db_query($sql)); + $this->ticket = null; + } + + function reload() { + return $this->load(); + } + + function __toString() { + return Format::htmlchars(sprintf('%s <%s>', $this->getName(), + $this->getEmail())); + } + + function getId() { + return $this->ht['id']; + } + + function isActive() { + return ($this->ht['isactive']); + } + + function getCreateDate() { + return $this->ht['created']; + } + + function getTicketId() { + return $this->ht['ticket_id']; + } + + function getTicket() { + if(!$this->ticket && $this->getTicketId()) + $this->ticket = Ticket::lookup($this->getTicketId()); + + return $this->ticket; + } + + function getUserId() { + return $this->ht['user_id']; + } + + function getUser() { + + if(!$this->user && $this->getUserId()) + $this->user = User::lookup($this->getUserId()); + + return $this->user; + } + + function remove() { + + $sql='DELETE FROM '.TICKET_COLLABORATOR_TABLE + .' WHERE id='.db_input($this->getId()) + .' LIMIT 1'; + + return (db_query($sql) && db_affected_rows()); + } + + static function add($info, &$errors) { + + if (!$info || !$info['ticketId'] || !$info['userId']) + $errors['err'] = 'Invalid or missing information'; + elseif (($c=self::lookup($info))) + $errors['err'] = sprintf('%s is already a collaborator', + $c->getName()); + + if ($errors) return false; + + $sql='INSERT INTO '.TICKET_COLLABORATOR_TABLE + .' SET updated=NOW() ' + .' ,isactive='.db_input(isset($info['isactive']) ? $info['isactive'] : 0) + .' ,ticket_id='.db_input($info['ticketId']) + .' ,user_id='.db_input($info['userId']); + + if(db_query($sql) && ($id=db_insert_id())) + return self::lookup($id); + + $errors['err'] = 'Unable to add collaborator. Internal error'; + + return false; + } + + static function forTicket($tid, $criteria=array()) { + + $collaborators = array(); + + $sql='SELECT id FROM '.TICKET_COLLABORATOR_TABLE + .' WHERE ticket_id='.db_input($tid); + + if(isset($criteria['isactive'])) + $sql.=' AND isactive='.db_input($criteria['isactive']); + + //TODO: sort by name of the user + + if(($res=db_query($sql)) && db_num_rows($res)) + while(list($id)=db_fetch_row($res)) + $collaborators[] = self::lookup($id); + + return $collaborators; + } + + static function getIdByInfo($info) { + + $sql='SELECT id FROM '.TICKET_COLLABORATOR_TABLE + .' WHERE ticket_id='.db_input($info['ticketId']) + .' AND user_id='.db_input($info['userId']); + + return db_result(db_query($sql)); + } + + static function lookup($criteria) { + + $id = is_numeric($criteria) + ? $criteria : self::getIdByInfo($criteria); + + return ($id + && ($c = new Collaborator($id)) + && $c->getId() == $id) + ? $c : null; + } +} +?> diff --git a/include/class.company.php b/include/class.company.php index 5122239f238b67064ee8c9e5655d2abb0172943d..c9aa22f04af3889ea22b564f06ffdf2dff936a71 100644 --- a/include/class.company.php +++ b/include/class.company.php @@ -47,12 +47,24 @@ class Company { } } - function asVar() { + function getInfo() { + return $this->getForm()->getClean(); + } + + function getName() { return $this->getVar('name'); } - function getInfo() { - return $this->getForm()->getClean(); + function asVar() { + return $this->getName(); + } + + function __toString() { + try { + if ($name = $this->getForm()->getAnswer('name')) + return $name->display(); + } catch (Exception $e) {} + return ''; } /** diff --git a/include/class.config.php b/include/class.config.php index 3ff7d1ef9787dfc58872fbe4f4f5ea79bb5b52c5..68704cd7b65523acb6f5610746b4e9741847c962 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -14,8 +14,6 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -require_once(INCLUDE_DIR.'class.email.php'); - class Config { var $config = array(); @@ -105,7 +103,9 @@ class Config { } function update($key, $value) { - if (!isset($this->config[$key])) + if (!$key) + return false; + elseif (!isset($this->config[$key])) return $this->create($key, $value); $setting = &$this->config[$key]; @@ -148,6 +148,11 @@ class OsticketConfig extends Config { 'allow_online_attachments_onlogin' => false, 'name_format' => 'full', # First Last 'auto_claim_tickets'=> true, + 'system_language' => 'en_US', + 'default_storage_bk' => 'D', + 'allow_client_updates' => false, + 'message_autoresponder_collabs' => true, + 'add_email_collabs' => true, ); function OsticketConfig($section=null) { @@ -293,6 +298,10 @@ class OsticketConfig extends Config { return $this->get('enable_html_thread'); } + function allowClientUpdates() { + return $this->get('allow_client_updates'); + } + function getClientTimeout() { return $this->getClientSessionTimeout(); } @@ -352,14 +361,13 @@ class OsticketConfig extends Config { function getDefaultEmail() { if(!$this->defaultEmail && $this->getDefaultEmailId()) - $this->defaultEmail=Email::lookup($this->getDefaultEmailId()); + $this->defaultEmail = Email::lookup($this->getDefaultEmailId()); return $this->defaultEmail; } function getDefaultEmailAddress() { - $email=$this->getDefaultEmail(); - return $email?$email->getAddress():null; + return ($email=$this->getDefaultEmail()) ? $email->getAddress() : null; } function getDefaultSLAId() { @@ -369,7 +377,7 @@ class OsticketConfig extends Config { function getDefaultSLA() { if(!$this->defaultSLA && $this->getDefaultSLAId()) - $this->defaultSLA=SLA::lookup($this->getDefaultSLAId()); + $this->defaultSLA = SLA::lookup($this->getDefaultSLAId()); return $this->defaultSLA; } @@ -380,15 +388,18 @@ class OsticketConfig extends Config { function getAlertEmail() { - if(!$this->alertEmail && $this->get('alert_email_id')) - $this->alertEmail= new Email($this->get('alert_email_id')); + if(!$this->alertEmail) + if(!($this->alertEmail = Email::lookup($this->getAlertEmailId()))) + $this->alertEmail = $this->getDefaultEmail(); + return $this->alertEmail; } function getDefaultSMTPEmail() { if(!$this->defaultSMTPEmail && $this->get('default_smtp_id')) - $this->defaultSMTPEmail= new Email($this->get('default_smtp_id')); + $this->defaultSMTPEmail = Email::lookup($this->get('default_smtp_id')); + return $this->defaultSMTPEmail; } @@ -522,6 +533,10 @@ class OsticketConfig extends Config { return ($this->get('use_email_priority')); } + function addCollabsViaEmail() { + return ($this->get('add_email_collabs')); + } + function getAdminEmail() { return $this->get('admin_email'); } @@ -551,6 +566,10 @@ class OsticketConfig extends Config { return ($this->get('message_autoresponder')); } + function notifyCollabsONNewMessage() { + return ($this->get('message_autoresponder_collabs')); + } + function notifyONNewStaffTicket() { return ($this->get('ticket_notice_active')); } @@ -707,6 +726,10 @@ class OsticketConfig extends Config { return ($this->allowAttachments() && $this->get('allow_email_attachments')); } + function getSystemLanguage() { + return $this->get('system_language'); + } + //TODO: change db field to allow_api_attachments - which will include email/json/xml attachments // terminology changed on the UI function allowAPIAttachments() { @@ -718,6 +741,10 @@ class OsticketConfig extends Config { return $this->get('upload_dir'); } + function getDefaultStorageBackendChar() { + return $this->get('default_storage_bk'); + } + function getVar($name) { return $this->get($name); } @@ -848,13 +875,15 @@ class OsticketConfig extends Config { if(!Validator::process($f, $vars, $errors) || $errors) return false; + if (isset($vars['default_storage_bk'])) + $this->update('default_storage_bk', $vars['default_storage_bk']); + return $this->updateAll(array( 'random_ticket_ids'=>$vars['random_ticket_ids'], 'default_priority_id'=>$vars['default_priority_id'], 'default_sla_id'=>$vars['default_sla_id'], 'max_open_tickets'=>$vars['max_open_tickets'], 'autolock_minutes'=>$vars['autolock_minutes'], - 'use_email_priority'=>isset($vars['use_email_priority'])?1:0, 'enable_captcha'=>isset($vars['enable_captcha'])?1:0, 'auto_claim_tickets'=>isset($vars['auto_claim_tickets'])?1:0, 'show_assigned_tickets'=>isset($vars['show_assigned_tickets'])?1:0, @@ -862,6 +891,7 @@ class OsticketConfig extends Config { 'show_related_tickets'=>isset($vars['show_related_tickets'])?1:0, 'hide_staff_name'=>isset($vars['hide_staff_name'])?1:0, 'enable_html_thread'=>isset($vars['enable_html_thread'])?1:0, + 'allow_client_updates'=>isset($vars['allow_client_updates'])?1:0, 'allow_attachments'=>isset($vars['allow_attachments'])?1:0, 'allowed_filetypes'=>strtolower(preg_replace("/\n\r|\r\n|\n|\r/", '',trim($vars['allowed_filetypes']))), 'max_file_size'=>$vars['max_file_size'], @@ -901,6 +931,8 @@ class OsticketConfig extends Config { 'enable_auto_cron'=>isset($vars['enable_auto_cron'])?1:0, 'enable_mail_polling'=>isset($vars['enable_mail_polling'])?1:0, 'strip_quoted_reply'=>isset($vars['strip_quoted_reply'])?1:0, + 'use_email_priority'=>isset($vars['use_email_priority'])?1:0, + 'add_email_collabs'=>isset($vars['add_email_collabs'])?1:0, 'reply_separator'=>$vars['reply_separator'], )); } @@ -971,6 +1003,7 @@ class OsticketConfig extends Config { return $this->updateAll(array( 'ticket_autoresponder'=>$vars['ticket_autoresponder'], 'message_autoresponder'=>$vars['message_autoresponder'], + 'message_autoresponder_collabs'=>$vars['message_autoresponder_collabs'], 'ticket_notice_active'=>$vars['ticket_notice_active'], 'overlimit_notice_active'=>$vars['overlimit_notice_active'], )); diff --git a/include/class.cron.php b/include/class.cron.php index 390ff0769117001d0714cd9f9874da74ada59b90..06d992cdcea53dcca5f02891014be3c6ce27a37a 100644 --- a/include/class.cron.php +++ b/include/class.cron.php @@ -3,7 +3,7 @@ class.cron.php Nothing special...just a central location for all cron calls. - + Peter Rotich <peter@osticket.com> Copyright (c) 2006-2013 osTicket http://www.osticket.com @@ -12,10 +12,12 @@ See LICENSE.TXT for details. TODO: The plan is to make cron jobs db based. - + vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ //TODO: Make it DB based! +require_once INCLUDE_DIR.'class.signal.php'; + class Cron { function MailFetcher() { @@ -27,7 +29,7 @@ class Cron { require_once(INCLUDE_DIR.'class.ticket.php'); require_once(INCLUDE_DIR.'class.lock.php'); Ticket::checkOverdue(); //Make stale tickets overdue - TicketLock::cleanup(); //Remove expired locks + TicketLock::cleanup(); //Remove expired locks } function PurgeLogs() { @@ -99,6 +101,8 @@ class Cron { self::CleanOrphanedFiles(); self::PurgeDrafts(); self::MaybeOptimizeTables(); + + Signal::send('cron', null); } } ?> diff --git a/include/class.crypto.php b/include/class.crypto.php index 92ab1e953f1b3786cac39e25e34acc7abfd59f9b..a9aebcd2f87b6e5e66b67a0c70ba0c6855ddd0cf 100644 --- a/include/class.crypto.php +++ b/include/class.crypto.php @@ -26,6 +26,8 @@ define('CRYPT_PHPSECLIB', 3); define('CRYPT_IS_WINDOWS', !strncasecmp(PHP_OS, 'WIN', 3)); + +require_once INCLUDE_DIR.'class.base32.php'; require_once PEAR_DIR.'Crypt/Hash.php'; require_once PEAR_DIR.'Crypt/AES.php'; diff --git a/include/class.dept.php b/include/class.dept.php index 8f1542b737d631a07024a6303dd80516963c5f28..44bf6561ebbb33ae92b357fdd7e2034f6aab4166 100644 --- a/include/class.dept.php +++ b/include/class.dept.php @@ -77,9 +77,11 @@ class Dept { } function getEmail() { + global $cfg; - if(!$this->email && $this->getEmailId()) - $this->email=Email::lookup($this->getEmailId()); + if(!$this->email) + if(!($this->email = Email::lookup($this->getEmailId())) && $cfg) + $this->email = $cfg->getDefaultEmail(); return $this->email; } @@ -138,19 +140,23 @@ class Dept { } function getTemplate() { + global $cfg; - if(!$this->template && $this->getTemplateId()) - $this->template = EmailTemplateGroup::lookup($this->getTemplateId()); + if (!$this->template) { + if (!($this->template = EmailTemplateGroup::lookup($this->getTemplateId()))) + $this->template = $cfg->getDefaultTemplate(); + } return $this->template; } function getAutoRespEmail() { - if(!$this->autorespEmail && $this->ht['autoresp_email_id'] && ($email=Email::lookup($this->ht['autoresp_email_id']))) - $this->autorespEmail=$email; - else // Defualt to dept email if autoresp is not specified or deleted. - $this->autorespEmail=$this->getEmail(); + if (!$this->autorespEmail) { + if (!$this->ht['autoresp_email_id'] + || !($this->autorespEmail = Email::lookup($this->ht['autoresp_email_id']))) + $this->autorespEmail = $this->getEmail(); + } return $this->autorespEmail; } diff --git a/include/class.dispatcher.php b/include/class.dispatcher.php index 44d710b55b1256bfe5709c0ddc0254a80695031a..d586fd0ba77e010549d72196bcaec0a5f94c44d2 100644 --- a/include/class.dispatcher.php +++ b/include/class.dispatcher.php @@ -100,9 +100,11 @@ class UrlMatcher { } function dispatch($url, $prev_args=null) { + # Remove named values from the match array - $this->matches = array_flip(array_intersect( - array_flip($this->matches), range(0,31))); + $f = array_filter(array_keys($this->matches), 'is_numeric'); + $this->matches = array_intersect_key($this->matches, array_flip($f)); + if (@get_class($this->func) == "Dispatcher") { # Trim the leading match off the $url and call the # sub-dispatcher. This will be the case for lines in the URL @@ -116,29 +118,31 @@ class UrlMatcher { substr($url, strlen($this->matches[0])), array_merge(($prev_args) ? $prev_args : array(), array_slice($this->matches, 1))); - } else { - # Drop the first item of the matches array (which is the whole - # matched url). Then merge in any initial arguments. - unset($this->matches[0]); - # Prepend received arguments (from a parent Dispatcher). This is - # different from the static args, which are postpended - if (is_array($prev_args)) - $args = array_merge($prev_args, $this->matches); - else $args = $this->matches; - # Add in static args specified in the constructor - $args = array_merge($args, $this->args); - # Apply the $prefix given - list($class, $func) = $this->apply_prefix(); - if ($class) { - # Create instance of the class, which is the first item, - # then call the method which is the second item - $func = array(new $class, $func); - } - if (!is_callable($func)) - Http::response(500, - 'Dispatcher compile error. Function not callable'); - return call_user_func_array($func, $args); } + + # Drop the first item of the matches array (which is the whole + # matched url). Then merge in any initial arguments. + unset($this->matches[0]); + + # Prepend received arguments (from a parent Dispatcher). This is + # different from the static args, which are postpended + if (is_array($prev_args)) + $args = array_merge($prev_args, $this->matches); + else $args = $this->matches; + # Add in static args specified in the constructor + $args = array_merge($args, $this->args); + # Apply the $prefix given + list($class, $func) = $this->apply_prefix(); + if ($class) { + # Create instance of the class, which is the first item, + # then call the method which is the second item + $func = array(new $class, $func); + } + + if (!is_callable($func)) + Http::response(500, 'Dispatcher compile error. Function not callable'); + + return call_user_func_array($func, $args); } /** * For the $prefix recieved by the constuctor, prepend it to the @@ -149,7 +153,10 @@ class UrlMatcher { function apply_prefix() { if (is_array($this->func)) { list($class, $func) = $this->func; } else { $func = $this->func; $class = ""; } - $class = $this->prefix . $class; + if (is_object($class)) + return array(false, $this->func); + if ($this->prefix) + $class = $this->prefix . $class; if (strpos($class, ":")) { list($file, $class) = explode(":", $class, 2); diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 6aba451e5748d344adb85c6e986430bf6ae953b4..0045b6a430e3d864d4b8792df143a187214451fe 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -271,7 +271,7 @@ class TicketForm extends DynamicForm { } } // Add fields from the standard ticket form to the ticket filterable fields -Filter::addSupportedMatches('Custom Fields', function() { +Filter::addSupportedMatches('Ticket Data', function() { $matches = array(); foreach (TicketForm::getInstance()->getFields() as $f) { if (!$f->hasData()) @@ -900,6 +900,10 @@ class SelectionField extends FormField { return ($item) ? $item : $value; } + function hasIdValue() { + return true; + } + function to_database($item) { if ($item instanceof DynamicListItem) return array($item->value, $item->id); @@ -945,6 +949,13 @@ class SelectionField extends FormField { } return $this->_choices; } + + function export($value) { + if ($value && is_numeric($value) + && ($item = DynamicListItem::lookup($value))) + return $item->toString(); + return $value; + } } class SelectionWidget extends ChoicesWidget { diff --git a/include/class.error.php b/include/class.error.php index ed5bf7e4c3d8d0384c63f0d8a4dbaa7af8b543a3..602304f763c5166b75b854c43597a893f6d42220 100644 --- a/include/class.error.php +++ b/include/class.error.php @@ -17,39 +17,32 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -class Error /* extends Exception */ { - var $title = ''; +class Error extends Exception { + static $title = ''; + static $sendAlert = true; - function Error($message) { - call_user_func_array(array($this,'__construct'), func_get_args()); - } function __construct($message) { global $ost; $message = str_replace(ROOT_DIR, '(root)/', $message); if ($ost->getConfig()->getLogLevel() == 3) - $message .= "\n\n" . $this->formatBacktrace(debug_backtrace()); + $message .= "\n\n" . $this->getBacktrace(); - $ost->logError($this->getTitle(), $message); + $ost->logError($this->getTitle(), $message, static::$sendAlert); } function getTitle() { - return get_class($this) . ": {$this->title}"; + return get_class($this) . ': ' . static::$title; } - function formatBacktrace($bt) { - $buffer = array(); - foreach ($bt as $i=>$frame) - $buffer[] = sprintf("#%d %s%s%s at [%s:%d]", $i, - $frame['class'], $frame['type'], $frame['function'], - str_replace(ROOT_DIR, '', $frame['file']), $frame['line']); - return implode("\n", $buffer); + function getBacktrace() { + return str_replace(ROOT_DIR, '(root)/', $this->getTraceAsString()); } } class InitialDataError extends Error { - var $title = 'Problem with install initial data'; + static $title = 'Problem with install initial data'; } function raise_error($message, $class=false) { @@ -57,4 +50,9 @@ function raise_error($message, $class=false) { new $class($message); } +// File storage backend exceptions +class IOException extends Error { + static $title = 'Unable to read resource content'; +} + ?> diff --git a/include/class.export.php b/include/class.export.php index 8c16501ecc8da623d237e0c59b51481cf4da71ae..b95a7d16d587541188d40d4cc6f45097b00b757f 100644 --- a/include/class.export.php +++ b/include/class.export.php @@ -16,12 +16,12 @@ class Export { - /* static */ function dumpQuery($sql, $headers, $how='csv', $filter=false) { + static function dumpQuery($sql, $headers, $how='csv', $options=array()) { $exporters = array( 'csv' => CsvResultsExporter, 'json' => JsonResultsExporter ); - $exp = new $exporters[$how]($sql, $headers, $filter); + $exp = new $exporters[$how]($sql, $headers, $options); return $exp->dump(); } @@ -32,13 +32,36 @@ class Export { # SQL is exported, but for something like tickets, we will need to # export attached messages, reponses, and notes, as well as # attachments associated with each, ... - /* static */ function dumpTickets($sql, $how='csv') { + static function dumpTickets($sql, $how='csv') { + // Add custom fields to the $sql statement + $cdata = $fields = $select = array(); + foreach (TicketForm::getInstance()->getFields() as $f) { + // Ignore core fields + if (in_array($f->get('name'), array('subject','priority'))) + continue; + // Ignore non-data fields + elseif (!$f->hasData() || $f->isPresentationOnly()) + continue; + + $name = $f->get('name') ? $f->get('name') : 'field_'.$f->get('id'); + $key = '__field_'.$f->get('id'); + // Fetch ID values for ID-based data + if ($f->hasIdValue()) { + $name .= '_id'; + } + $cdata[$key] = $f->get('label'); + $fields[$key] = $f; + $select[] = "cdata.`$name` AS __field_".$f->get('id'); + } + if ($select) + $sql = str_replace(' FROM ', ',' . implode(',', $select) . ' FROM ', $sql); return self::dumpQuery($sql, array( - 'ticketID' => 'Ticket Id', + 'number' => 'Ticket Number', 'created' => 'Date', 'subject' => 'Subject', 'name' => 'From', + 'email' => 'From Email', 'priority_desc' => 'Priority', 'dept_name' => 'Department', 'helptopic' => 'Help Topic', @@ -53,8 +76,17 @@ class Export { 'team' => 'Team Assigned', 'thread_count' => 'Thread Count', 'attachments' => 'Attachment Count', - ), - $how); + ) + $cdata, + $how, + array('modify' => function(&$record, $keys) use ($fields) { + foreach ($fields as $k=>$f) { + if (($i = array_search($k, $keys)) !== false) { + $record[$i] = $f->export($f->to_php($record[$i])); + } + } + return $record; + }) + ); } /* static */ function saveTickets($sql, $filename, $how='csv') { @@ -70,11 +102,12 @@ class Export { } class ResultSetExporter { - function ResultSetExporter($sql, $headers, $filter=false) { + function ResultSetExporter($sql, $headers, $options=array()) { $this->headers = array_values($headers); if ($s = strpos(strtoupper($sql), ' LIMIT ')) $sql = substr($sql, 0, $s); # TODO: If $filter, add different LIMIT clause to query + $this->options = $options; $this->_res = db_query($sql); if ($row = db_fetch_array($this->_res)) { $query_fields = array_keys($row); @@ -106,6 +139,10 @@ class ResultSetExporter { $record = array(); foreach ($this->lookups as $idx) $record[] = $row[$idx]; + + if (isset($this->options['modify']) && is_callable($this->options['modify'])) + $record = $this->options['modify']($record, $this->keys); + return $record; } diff --git a/include/class.faq.php b/include/class.faq.php index d009d58c3bce290d758fa05edeb12b3872c938e9..09d5b2dcb0280e1e39c31ff322d2247d19d6c5a5 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -195,7 +195,7 @@ class FAQ { if(($attachments=$this->attachments->getSeparates())) { foreach($attachments as $attachment ) { /* The h key must match validation in file.php */ - $hash=$attachment['hash'].md5($attachment['id'].session_id().$attachment['hash']); + $hash=$attachment['key'].md5($attachment['id'].session_id().strtolower($attachment['key'])); if($attachment['size']) $size=sprintf(' <small>(<i>%s</i>)</small>',Format::file_size($attachment['size'])); diff --git a/include/class.file.php b/include/class.file.php index 50ba68c79b0a92e8d572665cafb53398a6a8582c..e5a3db128fd69ba4c5b1fb8dc79492144fdb9c0d 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -11,6 +11,8 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ +require_once(INCLUDE_DIR.'class.signal.php'); +require_once(INCLUDE_DIR.'class.error.php'); class AttachmentFile { @@ -27,7 +29,7 @@ class AttachmentFile { if(!$id && !($id=$this->getId())) return false; - $sql='SELECT id, f.type, size, name, hash, f.created, ' + $sql='SELECT id, f.type, size, name, `key`, signature, ft, bk, f.created, ' .' count(DISTINCT a.object_id) as canned, count(DISTINCT t.ticket_id) as tickets ' .' FROM '.FILE_TABLE.' f ' .' LEFT JOIN '.ATTACHMENT_TABLE.' a ON(a.file_id=f.id) ' @@ -75,6 +77,10 @@ class AttachmentFile { return $this->ht['type']; } + function getBackend() { + return $this->ht['bk']; + } + function getMime() { return $this->getType(); } @@ -87,8 +93,14 @@ class AttachmentFile { return $this->ht['name']; } - function getHash() { - return $this->ht['hash']; + function getKey() { + return $this->ht['key']; + } + + function getSignature() { + $sig = $this->ht['signature']; + if (!$sig) return $this->getKey(); + return $sig; } function lastModified() { @@ -96,29 +108,42 @@ class AttachmentFile { } /** - * Retrieve a hash that can be sent to scp/file.php?h= in order to + * Retrieve a signature that can be sent to scp/file.php?h= in order to * download this file */ function getDownloadHash() { - return strtolower($this->getHash() . md5($this->getId().session_id().$this->getHash())); + return strtolower($this->getKey() + . md5($this->getId().session_id().strtolower($this->getKey()))); } function open() { - return new AttachmentChunkedData($this->id); + return FileStorageBackend::getInstance($this); } - function sendData() { + function sendData($redirect=true, $disposition='inline') { + $bk = $this->open(); + if ($redirect && $bk->sendRedirectUrl($disposition)) + return; + @ini_set('zlib.output_compression', 'Off'); - $file = $this->open(); - while ($chunk = $file->read()) - echo $chunk; + try { + $bk->passthru(); + } + catch (IOException $ex) { + Http::response(404, 'File not found'); + } } function getData() { # XXX: This is horrible, and is subject to php's memory # restrictions, etc. Don't use this function! ob_start(); - $this->sendData(); + try { + $this->sendData(false); + } + catch (IOException $ex) { + Http::response(404, 'File not found'); + } $data = &ob_get_contents(); ob_end_clean(); return $data; @@ -130,14 +155,14 @@ class AttachmentFile { if(!db_query($sql) || !db_affected_rows()) return false; - //Delete file data. - AttachmentChunkedData::deleteOrphans(); + if ($bk = $this->open()) + $bk->unlink(); return true; } function makeCacheable($ttl=86400) { - Http::cacheable($this->getHash(), $this->lastModified(), $ttl); + Http::cacheable($this->getSignature(), $this->lastModified(), $ttl); } function display($scale=false) { @@ -172,42 +197,71 @@ class AttachmentFile { } function download() { + $bk = $this->open(); + if ($bk->sendRedirectUrl('inline')) + return; $this->makeCacheable(); - - header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream')); - - $filename=basename($this->getName()); - $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']); - if (false !== strpos($user_agent,'msie') && false !== strpos($user_agent,'win')) - header('Content-Disposition: filename='.rawurlencode($filename).';'); - elseif (false !== strpos($user_agent, 'safari') && false === strpos($user_agent, 'chrome')) - // Safari and Safari only can handle the filename as is - header('Content-Disposition: filename='.str_replace(',', '', $filename).';'); - else - // Use RFC5987 - header("Content-Disposition: filename*=UTF-8''".rawurlencode($filename).';' ); - - header('Content-Transfer-Encoding: binary'); + Http::download($this->getName(), $this->getType() ?: 'application/octet-stream', + null, 'inline'); header('Content-Length: '.$this->getSize()); - $this->sendData(); + $this->sendData(false); exit(); } + function _getKeyAndHash($data=false, $file=false) { + if ($file) { + $sha1 = base64_encode(sha1_file($data, true)); + $md5 = base64_encode(md5_file($data, true)); + } + else { + $sha1 = base64_encode(sha1($data, true)); + $md5 = base64_encode(md5($data, true)); + } + + // Use 5 chars from the microtime() prefix and 27 chars from the + // sha1 hash. This should make a sufficiently strong unique key for + // file content. In the event there is a sha1 collision for data, it + // should be unlikely that there will be a collision for the + // microtime hash coincidently. Remove =, change + and / to chars + // better suited for URLs and filesystem paths + $prefix = base64_encode(sha1(microtime(), true)); + $key = str_replace( + array('=','+','/'), + array('','-','_'), + substr($prefix, 0, 5) . $sha1); + + // The hash is a 32-char value where the first half is from the last + // 16 chars from the SHA1 hash and the last 16 chars are the last 16 + // chars from the MD5 hash. This should provide for better + // resiliance against hash collisions and attacks against any one + // hash algorithm. Since we're using base64 encoding, with 6-bits + // per char, we should have a total hash strength of 192 bits. + $hash = str_replace( + array('=','+','/'), + array('','-','_'), + substr($sha1, 0, 16) . substr($md5, 0, 16)); + + return array($key, $hash); + } + /* Function assumes the files types have been validated */ function upload($file, $ft='T') { if(!$file['name'] || $file['error'] || !is_uploaded_file($file['tmp_name'])) return false; + list($key, $sig) = self::_getKeyAndHash($file['tmp_name'], true); + $info=array('type'=>$file['type'], 'filetype'=>$ft, 'size'=>$file['size'], 'name'=>$file['name'], - 'hash'=>MD5(MD5_FILE($file['tmp_name']).time()), - 'data'=>file_get_contents($file['tmp_name']) + 'key'=>$key, + 'signature'=>$sig, + 'tmp_name'=>$file['tmp_name'], ); - return AttachmentFile::save($info); + return AttachmentFile::save($info, $ft); } function uploadLogo($file, &$error, $aspect_ratio=3) { @@ -241,16 +295,34 @@ class AttachmentFile { return false; } - function save($file) { + function save(&$file, $ft='T') { - if (is_callable($file['data'])) - $file['data'] = $file['data'](); + if (isset($file['data'])) { + // Allow a callback function to delay or avoid reading or + // fetching ihe file contents + if (is_callable($file['data'])) + $file['data'] = $file['data'](); - if(!$file['hash']) - $file['hash'] = MD5(MD5($file['data']).time()); + list($key, $file['signature']) + = self::_getKeyAndHash($file['data']); + if (!$file['key']) + $file['key'] = $key; - if(!$file['size']) - $file['size'] = strlen($file['data']); + if (!isset($file['size'])) + $file['size'] = strlen($file['data']); + } + + // Check and see if the file is already on record + $sql = 'SELECT id, `key` FROM '.FILE_TABLE + .' WHERE signature='.db_input($file['signature']) + .' AND size='.db_input($file['size']); + + // If the record exists in the database already, a file with the + // same hash and size is already on file -- just return its ID + if (list($id, $key) = db_fetch_row(db_query($sql))) { + $file['key'] = $key; + return $id; + } if (!$file['type']) { $finfo = new finfo(FILEINFO_MIME_TYPE); @@ -270,27 +342,139 @@ class AttachmentFile { .',type='.db_input(strtolower($file['type'])) .',size='.db_input($file['size']) .',name='.db_input($file['name']) - .',hash='.db_input($file['hash']); + .',`key`='.db_input($file['key']) + .',ft='.db_input($ft ?: 'T') + .',signature='.db_input($file['signature']); - # XXX: ft does not exists during the upgrade when attachments are - # migrated! - if(isset($file['filetype'])) - $sql.=',ft='.db_input($file['filetype']); + if (!(db_query($sql) && ($id = db_insert_id()))) + return false; - if (!(db_query($sql) && ($id=db_insert_id()))) + if (!($f = AttachmentFile::lookup($id))) return false; - $data = new AttachmentChunkedData($id); - if (!$data->write($file['data'])) + // Note that this is preferred over $f->open() because the file does + // not have a valid backend configured yet. ::getBackendForFile() + // will consider the system configuration for storing the file + $bks = array(self::getBackendForFile($f)); + if (!$bks[0]->getBkChar() !== 'D') + $bks[] = new AttachmentChunkedData($f); + + // Consider the selected backen first and then save to database + // otherwise. + $succeeded = false; + foreach ($bks as $bk) { + if (isset($file['tmp_name'])) { + if ($bk->upload($file['tmp_name'])) { + $succeeded = true; break; + } + } + elseif ($bk->write($file['data']) && $bk->flush()) { + $succeeded = true; break; + } + // Fallthrough to default backend if different? + } + if (!$succeeded) { + // Unable to save data (weird) return false; + } - return $id; + $sql = 'UPDATE '.FILE_TABLE.' SET bk='.db_input($bk->getBkChar()) + .' WHERE id='.db_input($f->getId()); + db_query($sql); + + return $f->getId(); + } + + /** + * Migrate this file from the current backend to the backend specified. + * + * Parameters: + * $bk - (string) type char of the target storage backend. Use + * AttachmentStorageBackend::allRegistered() to get a list of type + * chars and associated class names + * + * Returns: + * True if the migration was successful and false otherwise. + */ + function migrate($bk) { + + // Copy the file to the new backend and hash the contents + $target = FileStorageBackend::lookup($bk, $this); + $source = $this->open(); + + // Initialize hashing algorithm to verify uploaded contents + $algos = $target->getNativeHashAlgos(); + $common_algo = 'sha1'; + if ($algos && is_array($algos)) { + $supported = hash_algos(); + foreach ($algos as $a) { + if (in_array(strtolower($a), $supported)) { + $common_algo = strtolower($a); + break; + } + } + } + $before = hash_init($common_algo); + // TODO: Make this resumable so that if the file cannot be migrated + // in the max_execution_time, the migration can be continued + // the next time the cron runs + while ($block = $source->read($target->getBlockSize())) { + hash_update($before, $block); + $target->write($block); + } + $target->flush(); + + // Ask the backend to generate its own hash if at all possible + if (!($target_hash = $target->getHashDigest($common_algo))) { + $after = hash_init($common_algo); + // Verify that the hash of the target file matches the hash of + // the source file + $target = FileStorageBackend::lookup($bk, $this); + while ($block = $target->read()) + hash_update($after, $block); + $target_hash = hash_final($after); + } + + if (hash_final($before) != $target_hash) { + $target->unlink(); + return false; + } + + $sql = 'UPDATE '.FILE_TABLE.' SET bk=' + .db_input($target->getBkChar()) + .' WHERE id='.db_input($this->getId()); + if (!db_query($sql) || db_affected_rows()!=1) + return false; + + return $source->unlink(); + } + + /** + * Considers the system's configuration for file storage selection based + * on the file information and purpose (FAQ attachment, image, etc). + * + * Parameters: + * $file - (hasharray) file information which would be passed to + * ::save() for instance. + * + * Returns: + * Instance<FileStorageBackend> backend selected based on the file + * received. + */ + static function getBackendForFile($file) { + global $cfg; + + if (!$cfg) + return new AttachmentChunkedData($file); + + $char = $cfg->getDefaultStorageBackendChar(); + return FileStorageBackend::lookup($char, $file); } /* Static functions */ function getIdByHash($hash) { - $sql='SELECT id FROM '.FILE_TABLE.' WHERE hash='.db_input($hash); + $sql='SELECT id FROM '.FILE_TABLE.' WHERE `key`='.db_input($hash); if(($res=db_query($sql)) && db_num_rows($res)) list($id)=db_fetch_row($res); @@ -366,19 +550,23 @@ class AttachmentFile { */ /* static */ function deleteOrphans() { - $sql = 'DELETE FROM '.FILE_TABLE.' WHERE id NOT IN (' + // XXX: Allow plugins to define filetypes which do not represent + // files attached to tickets or other things in the attachment + // table and are not logos + $sql = 'SELECT id FROM '.FILE_TABLE.' WHERE id NOT IN (' .'SELECT file_id FROM '.TICKET_ATTACHMENT_TABLE .' UNION ' .'SELECT file_id FROM '.ATTACHMENT_TABLE .") AND `ft` = 'T'"; - db_query($sql); + if (!($res = db_query($sql))) + return false; - //Delete orphaned chuncked data! - AttachmentChunkedData::deleteOrphans(); + while (list($id) = db_fetch_row($res)) + if (($file = self::lookup($id)) && !$file->delete()) + break; return true; - } /* static */ @@ -393,31 +581,198 @@ class AttachmentFile { } } +class FileStorageBackend { + var $meta; + static $desc = false; + static $registry; + static $blocksize = 131072; + + /** + * All storage backends should call this function during the request + * bootstrap phase. + */ + static function register($typechar, $class) { + self::$registry[$typechar] = $class; + } + + static function allRegistered() { + return self::$registry; + } + + /** + * Retrieves the type char registered for this storage backend's class. + * Null is returned if the backend is not properly registered. + */ + function getBkChar() { + foreach (self::$registry as $tc=>$class) + if ($this instanceof $class) + return $tc; + } + + static function isRegistered($type) { + return isset(self::$registry[$type]); + } + + static function lookup($type, $file=null) { + if (!isset(self::$registry[$type])) + throw new Exception("No such backend registered"); + + $class = self::$registry[$type]; + return new $class($file); + } + + static function getInstance($file) { + if (!isset(self::$registry[$file->getBackend()])) + throw new Exception("No such backend registered"); + + $class = self::$registry[$file->getBackend()]; + return new $class($file); + } + + /** + * Returns the optimal block size for the backend. When migrating, this + * size blocks would be best for sending to the ::write() method + */ + function getBlockSize() { + return static::$blocksize; + } + + /** + * Create an instance of the storage backend linking the related file. + * Information about the file metadata is accessible via the received + * filed object. + */ + function __construct($meta) { + $this->meta = $meta; + } + + /** + * Commit file to the storage backend. This method is used if the + * backend cannot support writing a file directly. Otherwise, the + * ::upload($file) method is preferred. + * + * Parameters: + * $data - (string|binary) file contents to be written to the backend + */ + function write($data) { + return false; + } + + /** + * Called after all the blocks are sent to the ::write() method. This + * method should return boolean FALSE if flushing the data was + * somehow inhibited. + */ + function flush() { + return true; + } + + /** + * Upload a file to the backend. This method is preferred over ::write() + * for files which are uploaded or are otherwise available out of + * memory. The backend is encouraged to avoid reading the entire + * contents into memory. + */ + function upload($filepath) { + return $this->write(file_get_contents($filepath)); + } + + /** + * Returns data from the backend, optionally returning only the number + * of bytes indicated at the specified offset. If the data is available + * in chunks, one chunk may be returned at a time. The backend should + * return boolean false when no more chunks are available. + */ + function read($amount=0, $offset=0) { + return false; + } + + /** + * Convenience method to send all the file to standard output + */ + function passthru() { + while ($block = $this->read()) + echo $block; + } + + /** + * If the data is not stored or not available locally, a redirect + * response can be sent to the user agent indicating the actual HTTP + * location of the data. + * + * If the data is available locally, this method should return boolean + * false to indicate that the read() method should be used to retrieve + * the data and broker it to the user agent. + */ + function sendRedirectUrl($disposition='inline') { + return false; + } + + /** + * Requests the backend to remove the file contents. + */ + function unlink() { + return false; + } + + /** + * Fetches a list of hash algorithms that are supported transparently + * through the ::write() and ::upload() methods. After writing or + * uploading file content, the ::getHashDigest($algo) method can be + * called to get a hash of the remote content without fetching the + * entire data stream to verify the content locally. + */ + function getNativeHashAlgos() { + return array(); + } + + /** + * Returns a hash of the content calculated remotely by the storage + * backend. If this method fails, the hash chould be calculated by + * downloading the content and hashing locally + */ + function getHashDigest($algo) { + return false; + } +} + + /** - * Attachments stored in the database are cut into 256kB chunks and stored + * Attachments stored in the database are cut into 500kB chunks and stored * in the FILE_CHUNK_TABLE to overcome the max_allowed_packet limitation of * LOB fields in the MySQL database */ define('CHUNK_SIZE', 500*1024); # Beware if you change this... -class AttachmentChunkedData { - function AttachmentChunkedData($file) { - $this->_file = $file; - $this->_pos = 0; +class AttachmentChunkedData extends FileStorageBackend { + static $desc = "In the database"; + static $blocksize = CHUNK_SIZE; + + function __construct($file) { + $this->file = $file; + $this->_chunk = 0; + $this->_buffer = false; } function length() { list($length) = db_fetch_row(db_query( 'SELECT SUM(LENGTH(filedata)) FROM '.FILE_CHUNK_TABLE - .' WHERE file_id='.db_input($this->_file))); + .' WHERE file_id='.db_input($this->file->getId()))); return $length; } - function read() { + function read($amount=CHUNK_SIZE, $offset=0) { # Read requested length of data from attachment chunks - list($buffer) = @db_fetch_row(db_query( - 'SELECT filedata FROM '.FILE_CHUNK_TABLE.' WHERE file_id=' - .db_input($this->_file).' AND chunk_id='.$this->_pos++)); - return $buffer; + while (strlen($this->_buffer) < $amount + $offset) { + list($buf) = @db_fetch_row(db_query( + 'SELECT filedata FROM '.FILE_CHUNK_TABLE.' WHERE file_id=' + .db_input($this->file->getId()).' AND chunk_id='.$this->_chunk++)); + if (!$buf) + break; + $this->_buffer .= $buf; + } + $chunk = substr($this->_buffer, $offset, $amount); + $this->_buffer = substr($this->_buffer, $offset + $amount); + return $chunk; } function write($what, $chunk_size=CHUNK_SIZE) { @@ -427,28 +782,20 @@ class AttachmentChunkedData { if (!$block) break; if (!db_query('REPLACE INTO '.FILE_CHUNK_TABLE .' SET filedata=0x'.bin2hex($block).', file_id=' - .db_input($this->_file).', chunk_id='.db_input($this->_pos++))) + .db_input($this->file->getId()).', chunk_id='.db_input($this->_chunk++))) return false; $offset += strlen($block); } - return $this->_pos; + return $this->_chunk; } - function deleteOrphans() { - $deleted = 0; - $sql = 'SELECT c.file_id, c.chunk_id FROM '.FILE_CHUNK_TABLE.' c ' - . ' LEFT JOIN '.FILE_TABLE.' f ON(f.id=c.file_id) ' - . ' WHERE f.id IS NULL'; - - $res = db_query($sql); - while (list($file_id, $chunk_id) = db_fetch_row($res)) { - db_query('DELETE FROM '.FILE_CHUNK_TABLE - .' WHERE file_id='.db_input($file_id) - .' AND chunk_id='.db_input($chunk_id)); - $deleted += db_affected_rows(); - } - return $deleted; + function unlink() { + db_query('DELETE FROM '.FILE_CHUNK_TABLE + .' WHERE file_id='.db_input($this->file->getId())); + return db_affected_rows() > 0; } } +FileStorageBackend::register('D', 'AttachmentChunkedData'); + ?> diff --git a/include/class.filter.php b/include/class.filter.php index 80081aa8ace454e5d1368b60522e4263ae6188bd..0d9382771e518b2a2e979f2c388e717133cb7f10 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -19,13 +19,14 @@ class Filter { var $ht; static $match_types = array( - 'Basic Fields' => array( + 'User Information' => array( 'name' => 'Name', 'email' => 'Email', - 'subject' => 'Subject', - 'body' => 'Body/Text', + ), + 'Email Meta-Data' => array( 'reply-to' => 'Reply-To Email', 'reply-to-name' => 'Reply-To Name', + 'addressee' => 'Addressee (To and Cc)', ), ); @@ -242,12 +243,14 @@ class Filter { $how = array( # how => array(function, null or === this, null or !== this) - 'equal' => array('strcmp', 0), - 'not_equal' => array('strcmp', null, 0), - 'contains' => array('strpos', null, false), - 'dn_contain'=> array('strpos', false), - 'starts' => array('strpos', 0), - 'ends' => array('endsWith', true) + 'equal' => array('strcasecmp', 0), + 'not_equal' => array('strcasecmp', null, 0), + 'contains' => array('stripos', null, false), + 'dn_contain'=> array('stripos', false), + 'starts' => array('stripos', 0), + 'ends' => array('iendsWith', true), + 'match' => array('pregMatchB', 1), + 'not_match' => array('pregMatchB', null, 0), ); $match = false; @@ -260,12 +263,8 @@ class Filter { foreach ($this->getRules() as $rule) { if (!isset($how[$rule['h']])) continue; list($func, $pos, $neg) = $how[$rule['h']]; - # TODO: convert $what and $rule['v'] to mb_strtoupper and do - # case-sensitive, binary-safe comparisons. Would be really - # nice to do $rule['v'] on the database side for - # performance -- but ::getFlatRules() is a blocker - $result = call_user_func($func, strtoupper($what[$rule['w']]), - strtoupper($rule['v'])); + + $result = call_user_func($func, $what[$rule['w']], $rule['v']); if (($pos === null && $result !== $neg) or ($result === $pos)) { # Match. $match = true; @@ -341,7 +340,9 @@ class Filter { 'contains'=> 'Contains', 'dn_contain'=> 'Does Not Contain', 'starts'=> 'Starts With', - 'ends'=> 'Ends With' + 'ends'=> 'Ends With', + 'match'=> 'Matches Regex', + 'not_match'=> 'Does Not Match Regex', ); } @@ -404,6 +405,14 @@ class Filter { $rules=array(); for($i=1; $i<=25; $i++) { //Expecting no more than 25 rules... if($vars["rule_w$i"] || $vars["rule_h$i"]) { + // Check for REGEX compile errors + if (in_array($vars["rule_h$i"], array('match','not_match'))) { + $wrapped = "/".$vars["rule_v$i"]."/iu"; + if (false === @preg_match($vars["rule_v$i"], ' ') + && (false !== @preg_match($wrapped, ' '))) + $vars["rule_v$i"] = $wrapped; + } + if(!$vars["rule_w$i"] || !in_array($vars["rule_w$i"],$matches)) $errors["rule_$i"]='Invalid match selection'; elseif(!$vars["rule_h$i"] || !in_array($vars["rule_h$i"],$types)) @@ -414,6 +423,12 @@ class Filter { && $vars["rule_h$i"]=='equal' && !Validator::is_email($vars["rule_v$i"])) $errors["rule_$i"]='Valid email required for the match type'; + elseif (in_array($vars["rule_h$i"], array('match','not_match')) + && (false === @preg_match($vars["rule_v$i"], ' '))) + $errors["rule_$i"] = sprintf('Regex compile error: (#%s)', + preg_last_error()); + + else //for everything-else...we assume it's valid. $rules[]=array('what'=>$vars["rule_w$i"], 'how'=>$vars["rule_h$i"],'val'=>$vars["rule_v$i"]); @@ -679,6 +694,13 @@ class TicketFilter { if (in_array($k, $interest)) $this->vars[$k] = trim($v); } + if (isset($vars['recipients']) && $vars['recipients']) { + foreach ($vars['recipients'] as $r) { + $this->vars['addressee'][] = $r['name']; + $this->vars['addressee'][] = $r['email']; + } + $this->vars['addressee'] = implode(' ', $this->vars['addressee']); + } //Init filters. $this->build(); @@ -910,13 +932,17 @@ class TicketFilter { * Returns TRUE if the haystack ends with needle and FALSE otherwise. * Thanks, http://stackoverflow.com/a/834355 */ -function endsWith($haystack, $needle) +function iendsWith($haystack, $needle) { - $length = strlen($needle); + $length = mb_strlen($needle); if ($length == 0) { return true; } - return (substr($haystack, -$length) === $needle); + return (strcasecmp(mb_substr($haystack, -$length), $needle) === 0); +} + +function pregMatchB($subject, $pattern) { + return preg_match($pattern, $subject); } ?> diff --git a/include/class.format.php b/include/class.format.php index 048b886b7913279b6414c904f3d7ab89eb5204e2..e3f7b415a7553f6f77e6da85b31aff4d2c2f9ba5 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -34,21 +34,24 @@ class Format { function encode($text, $charset=null, $encoding='utf-8') { //Try auto-detecting charset/encoding - if(!$charset && function_exists('mb_detect_encoding')) + if (!$charset && function_exists('mb_detect_encoding')) $charset = mb_detect_encoding($text); // Cleanup - incorrect, bogus, or ambiguous charsets - if($charset && in_array(strtolower(trim($charset)), + // ISO-8859-1 is assumed for empty charset. + if (!$charset || in_array(strtolower(trim($charset)), array('default','x-user-defined','iso','us-ascii'))) $charset = 'ISO-8859-1'; $original = $text; - if(function_exists('iconv') && $charset) + if (function_exists('iconv')) $text = iconv($charset, $encoding.'//IGNORE', $text); - elseif(function_exists('mb_convert_encoding') && $charset && $encoding) + elseif (function_exists('mb_convert_encoding')) $text = mb_convert_encoding($text, $encoding, $charset); - elseif(!strcasecmp($encoding, 'utf-8')) //forced blind utf8 encoding. - $text = function_exists('imap_utf8')?imap_utf8($text):utf8_encode($text); + elseif (!strcasecmp($encoding, 'utf-8') + && function_exists('utf8_encode') + && !strcasecmp($charset, 'ISO-8859-1')) + $text = utf8_encode($text); // If $text is false, then we have a (likely) invalid charset, use // the original text and assume 8-bit (latin-1 / iso-8859-1) @@ -176,7 +179,7 @@ class Format { if (isset($attributes['style'])) { $styles = explode(';', $attributes['style']); foreach ($styles as $i=>$s) { - list($prop, $val) = explode(':', $s); + @list($prop, $val) = explode(':', $s); if (!$val || !$prop || $prop[0] == '-') unset($styles[$i]); } @@ -215,7 +218,7 @@ class Format { 'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; src: cid, http, https, data', 'hook_tag' => function($e, $a=0) { return Format::__html_cleanup($e, $a); }, 'elements' => '*+iframe', - 'spec' => 'iframe=-*,height,width,type,src(match="`^(https?:)?//(www\.)?(youtube|dailymotion|vimeo)\.com/`i"),frameborder;', + 'spec' => 'iframe=-*,height,width,type,src(match="`^(https?:)?//(www\.)?(youtube|dailymotion|vimeo)\.com/`i"),frameborder; div=data-mid', ); return Format::html($html, $config); @@ -381,7 +384,7 @@ class Format { function viewableImages($html, $script='image.php') { - return preg_replace_callback('/"cid:([\\w.-]{32})"/', + return preg_replace_callback('/"cid:([\w._-]{32})"/', function($match) use ($script) { $hash = $match[1]; if (!($file = AttachmentFile::lookup($hash))) diff --git a/include/class.forms.php b/include/class.forms.php index ca4cf44a7a38e31eea60365ffc23139e68603275..2bbb04a66559d7264816968e2ce023b9b271e083 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -51,9 +51,12 @@ class Form { } function getField($name) { - foreach($this->getFields() as $f) + $fields = $this->getFields(); + foreach($fields as $f) if(!strcasecmp($f->get('name'), $name)) return $f; + if (isset($fields[$name])) + return $fields[$name]; } function getTitle() { return $this->title; } @@ -82,9 +85,12 @@ class Form { if (!$this->_clean) { $this->_clean = array(); foreach ($this->getFields() as $key=>$field) { + if (!$field->hasData()) + continue; $this->_clean[$key] = $this->_clean[$field->get('name')] = $field->getClean(); } + unset($this->_clean[""]); } return $this->_clean; } @@ -129,14 +135,14 @@ class FormField { static $types = array( 'Basic Fields' => array( - 'text' => array('Short Answer', TextboxField), - 'memo' => array('Long Answer', TextareaField), - 'thread' => array('Thread Entry', ThreadEntryField, false), - 'datetime' => array('Date and Time', DatetimeField), - 'phone' => array('Phone Number', PhoneField), - 'bool' => array('Checkbox', BooleanField), - 'choices' => array('Choices', ChoiceField), - 'break' => array('Section Break', SectionBreakField), + 'text' => array('Short Answer', 'TextboxField'), + 'memo' => array('Long Answer', 'TextareaField'), + 'thread' => array('Thread Entry', 'ThreadEntryField', false), + 'datetime' => array('Date and Time', 'DatetimeField'), + 'phone' => array('Phone Number', 'PhoneField'), + 'bool' => array('Checkbox', 'BooleanField'), + 'choices' => array('Choices', 'ChoiceField'), + 'break' => array('Section Break', 'SectionBreakField'), ), ); static $more_types = array(); @@ -305,6 +311,15 @@ class FormField { return Format::htmlchars($this->toString($value)); } + /** + * Returns a value suitable for exporting to a foreign system. Mostly + * useful for things like dates and phone numbers which should be + * formatted using a standard when exported + */ + function export($value) { + return $this->toString($value); + } + function getLabel() { return $this->get('label'); } /** @@ -467,7 +482,8 @@ class FormField { if (!static::$widget) throw new Exception('Widget not defined for this field'); if (!isset($this->_widget)) { - $this->_widget = new static::$widget($this); + $wc = $this->get('widget') ? $this->get('widget') : static::$widget; + $this->_widget = new $wc($this); $this->_widget->parseValue(); } return $this->_widget; @@ -531,6 +547,18 @@ class TextboxField extends FormField { } } +class PasswordField extends TextboxField { + static $widget = 'PasswordWidget'; + + function to_database($value) { + return Crypto::encrypt($value, SECRET_SALT, $this->getFormName()); + } + + function to_php($value) { + return Crypto::decrypt($value, SECRET_SALT, $this->getFormName()); + } +} + class TextareaField extends FormField { static $widget = 'TextareaWidget'; @@ -746,6 +774,16 @@ class DatetimeField extends FormField { return Format::date($format, $value); } + function export($value) { + $config = $this->getConfiguration(); + if (!$value) + return ''; + elseif ($config['gmt']) + return Format::userdate('Y-m-d H:i:s', $value); + else + return Format::date('Y-m-d H:i:s', $value); + } + function getConfigurationOptions() { return array( 'time' => new BooleanField(array( @@ -910,6 +948,8 @@ class Widget { } class TextboxWidget extends Widget { + static $input_type = 'text'; + function render() { $config = $this->field->getConfiguration(); if (isset($config['size'])) @@ -922,9 +962,10 @@ class TextboxWidget extends Widget { $autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"'; ?> <span style="display:inline-block"> - <input type="text" id="<?php echo $this->name; ?>" - <?php echo $size . " " . $maxlength - .' '.$classes.' '.$autocomplete + <input type="<?php echo static::$input_type; ?>" + id="<?php echo $this->name; ?>" + <?php echo $size . " " . $maxlength; ?> + <?php echo $classes.' '.$autocomplete .' placeholder="'.$config['placeholder'].'"'; ?> name="<?php echo $this->name; ?>" value="<?php echo Format::htmlchars($this->value); ?>"/> @@ -933,6 +974,19 @@ class TextboxWidget extends Widget { } } +class PasswordWidget extends TextboxWidget { + static $input_type = 'password'; + + function parseValue() { + // Show empty box unless failed POST + if ($_SERVER['REQUEST_METHOD'] == 'POST' + && $this->field->getForm()->isValid()) + parent::parseValue(); + else + $this->value = ''; + } +} + class TextareaWidget extends Widget { function render() { $config = $this->field->getConfiguration(); diff --git a/include/class.http.php b/include/class.http.php index ef917845d76a1f943e022167304eb7a2b0d131c5..3baea958a9495b3e8967510818f36cb561069ec4 100644 --- a/include/class.http.php +++ b/include/class.http.php @@ -55,6 +55,9 @@ class Http { }else{ header("Location: $url"); } + print('<html></html>'); + flush(); + ob_flush(); exit; } @@ -73,20 +76,34 @@ class Http { } } - function download($filename, $type, $data=null) { - header('Pragma: public'); + /** + * Creates the filename=... part of the Content-Disposition header. This + * is browser dependent, so the user agent is inspected to determine the + * best encoding format for the filename + */ + function getDispositionFilename($filename) { + $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']); + if (false !== strpos($user_agent,'msie') + && false !== strpos($user_agent,'win')) + return 'filename='.rawurlencode($filename); + elseif (false !== strpos($user_agent, 'safari') + && false === strpos($user_agent, 'chrome')) + // Safari and Safari only can handle the filename as is + return 'filename='.str_replace(',', '', $filename); + else + // Use RFC5987 + return "filename*=UTF-8''".rawurlencode($filename); + } + + function download($filename, $type, $data=null, $disposition='attachment') { + header('Pragma: private'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Cache-Control: public'); + header('Cache-Control: private'); header('Content-Type: '.$type); - $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']); - if (strpos($user_agent,'msie') !== false - && strpos($user_agent,'win') !== false) { - header('Content-Disposition: filename="'.basename($filename).'";'); - } else { - header('Content-Disposition: attachment; filename="' - .basename($filename).'"'); - } + header(sprintf('Content-Disposition: %s; %s', + $disposition, + self::getDispositionFilename(basename($filename)))); header('Content-Transfer-Encoding: binary'); if ($data !== null) { header('Content-Length: '.strlen($data)); diff --git a/include/class.i18n.php b/include/class.i18n.php index 3d721e8eb7d2769884aae68e1fbb3cbb29ac2828..7335495d5bd75ecdc9547a27d5af35f8f5de5076 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -16,7 +16,6 @@ **********************************************************************/ require_once INCLUDE_DIR.'class.error.php'; require_once INCLUDE_DIR.'class.yaml.php'; -require_once INCLUDE_DIR.'class.config.php'; class Internationalization { @@ -43,7 +42,6 @@ class Internationalization { function loadDefaultData() { # notrans -- do not translate the contents of this array $models = array( - 'email_template_group.yaml' => 'EmailTemplateGroup', 'department.yaml' => 'Dept', 'sla.yaml' => 'SLA', 'form.yaml' => 'DynamicForm', @@ -77,6 +75,7 @@ class Internationalization { } // Configuration + require_once INCLUDE_DIR.'class.config.php'; if (($tpl = $this->getTemplate('config.yaml')) && ($data = $tpl->getData())) { foreach ($data as $section=>$items) { @@ -101,6 +100,8 @@ class Internationalization { if (db_query($sql) && ($id = db_insert_id())) $_config->set("{$type}_page_id", $id); } + // Default Language + $_config->set('system_language', $this->langs[0]); // Canned response examples if (($tpl = $this->getTemplate('templates/premade.yaml')) @@ -118,6 +119,13 @@ class Internationalization { // Email templates // TODO: Lookup tpl_id + if ($objects = $this->getTemplate('email_template_group.yaml')->getData()) { + foreach ($objects as $o) { + $o['lang_id'] = $this->langs[0]; + $tpl = EmailTemplateGroup::create($o, $errors); + } + } + // This shouldn't be necessary $tpl = EmailTemplateGroup::lookup(1); foreach ($tpl::$all_names as $name=>$info) { if (($tp = $this->getTemplate("templates/email/$name.yaml")) @@ -131,6 +139,122 @@ class Internationalization { } } } + + static function availableLanguages($base=I18N_DIR) { + $langs = (include I18N_DIR . 'langs.php'); + + // Consider all subdirectories and .phar files in the base dir + $dirs = glob(I18N_DIR . '*', GLOB_ONLYDIR | GLOB_NOSORT); + $phars = glob(I18N_DIR . '*.phar', GLOB_NOSORT); + + $installed = array(); + foreach (array_merge($dirs, $phars) as $f) { + $base = basename($f, '.phar'); + @list($code, $locale) = explode('_', $base); + if (isset($langs[$code])) { + $installed[strtolower($base)] = + $langs[$code] + array( + 'lang' => $code, + 'locale' => $locale, + 'path' => $f, + 'code' => $base, + 'desc' => sprintf("%s%s (%s)", + $langs[$code]['nativeName'], + $locale ? sprintf(' - %s', $locale) : '', + $langs[$code]['name']), + ); + } + } + usort($installed, function($a, $b) { return strcasecmp($a['code'], $b['code']); }); + + return $installed; + } + + // TODO: Move this to the REQUEST class or some middleware when that + // exists. + // Algorithm borrowed from Drupal 7 (locale.inc) + static function getDefaultLanguage() { + global $cfg; + + if (empty($_SERVER["HTTP_ACCEPT_LANGUAGE"])) + return $cfg->getSystemLanguage(); + + $languages = self::availableLanguages(); + + // The Accept-Language header contains information about the + // language preferences configured in the user's browser / operating + // system. RFC 2616 (section 14.4) defines the Accept-Language + // header as follows: + // Accept-Language = "Accept-Language" ":" + // 1#( language-range [ ";" "q" "=" qvalue ] ) + // language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" + $browser_langcodes = array(); + $matches = array(); + if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', + trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + // We can safely use strtolower() here, tags are ASCII. + // RFC2616 mandates that the decimal part is no more than three + // digits, so we multiply the qvalue by 1000 to avoid floating + // point comparisons. + $langcode = strtolower($match[1]); + $qvalue = isset($match[2]) ? (float) $match[2] : 1; + $browser_langcodes[$langcode] = (int) ($qvalue * 1000); + } + } + + // We should take pristine values from the HTTP headers, but + // Internet Explorer from version 7 sends only specific language + // tags (eg. fr-CA) without the corresponding generic tag (fr) + // unless explicitly configured. In that case, we assume that the + // lowest value of the specific tags is the value of the generic + // language to be as close to the HTTP 1.1 spec as possible. + // + // References: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx + asort($browser_langcodes); + foreach ($browser_langcodes as $langcode => $qvalue) { + $generic_tag = strtok($langcode, '-'); + if (!isset($browser_langcodes[$generic_tag])) { + $browser_langcodes[$generic_tag] = $qvalue; + } + } + + // Find the enabled language with the greatest qvalue, following the rules + // of RFC 2616 (section 14.4). If several languages have the same qvalue, + // prefer the one with the greatest weight. + $best_match_langcode = FALSE; + $max_qvalue = 0; + foreach ($languages as $langcode => $language) { + // Language tags are case insensitive (RFC2616, sec 3.10). + // We use _ as the location separator + $langcode = str_replace('_','-',strtolower($langcode)); + + // If nothing matches below, the default qvalue is the one of the wildcard + // language, if set, or is 0 (which will never match). + $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0; + + // Find the longest possible prefix of the browser-supplied language + // ('the language-range') that matches this site language ('the language tag'). + $prefix = $langcode; + do { + if (isset($browser_langcodes[$prefix])) { + $qvalue = $browser_langcodes[$prefix]; + break; + } + } while ($prefix = substr($prefix, 0, strrpos($prefix, '-'))); + + // Find the best match. + if ($qvalue > $max_qvalue) { + $best_match_langcode = $language['code']; + $max_qvalue = $qvalue; + } + } + + return $best_match_langcode; + } } class DataTemplate { @@ -153,6 +277,12 @@ class DataTemplate { $this->filepath = Misc::realpath("{$this->base}/$l/$path"); break; } + elseif (Phar::isValidPharFilename("{$this->base}/$l.phar") + && file_exists("phar://{$this->base}/$l.phar/$path")) { + $this->lang = $l; + $this->filepath = "phar://{$this->base}/$l.phar/$path"; + break; + } } } diff --git a/include/class.mailer.php b/include/class.mailer.php index 92b612781833e2541b2f1c7b6488d337c8080ec0..4adc2c4b27138f9a1df6c6dd1c32087ca5e251a0 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -100,19 +100,22 @@ class Mailer { $subject = preg_replace("/(\r\n|\r|\n)/s",'', trim($subject)); /* Message ID - generated for each outgoing email */ - $messageId = sprintf('<%s-%s>', Misc::randCode(16), - ($this->getEmail()?$this->getEmail()->getEmail():'@osTicketMailer')); + $messageId = sprintf('<%s-%s-%s>', + substr(md5('mail'.SECRET_SALT), -9), + Misc::randCode(9), + ($this->getEmail()?$this->getEmail()->getEmail():'@osTicketMailer')); $headers = array ( - 'From' => $this->getFromAddress(), - 'To' => $to, - 'Subject' => $subject, - 'Date'=> date('D, d M Y H:i:s O'), - 'Message-ID' => $messageId, - 'X-Mailer' =>'osTicket Mailer', - 'Return-Path' => $this->getEmail()->getEmail(), - ); + 'From' => $this->getFromAddress(), + 'To' => $to, + 'Subject' => $subject, + 'Date'=> date('D, d M Y H:i:s O'), + 'Message-ID' => $messageId, + 'X-Mailer' =>'osTicket Mailer', + ); + if ($this->getEmail() instanceof Email) + $headers['Return-Path'] = $this->getEmail()->getEmail(); //Bulk. if (isset($options['bulk']) && $options['bulk']) @@ -151,10 +154,18 @@ class Mailer { // then assume that it needs html processing to create a valid text // body $isHtml = true; + $mid_token = (isset($options['thread'])) + ? $options['thread']->asMessageId($to) : ''; if (!(isset($options['text']) && $options['text'])) { + if ($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()) + && (!isset($options['reply-tag']) || $options['reply-tag'])) + $message = "<div style=\"display:none\" + data-mid=\"$mid_token\">$tag<br/><br/></div>$message"; // Make sure nothing unsafe has creeped into the message $message = Format::safe_html($message); //XXX?? - $mime->setTXTBody(Format::html2text($message, 90, false)); + $txtbody = rtrim(Format::html2text($message, 90, false)) + . ($mid_token ? "\nRef-Mid: $mid_token\n" : ''); + $mime->setTXTBody($txtbody); } else { $mime->setTXTBody($message); diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index 73d07a6f90114829a96f8951120d0af11712c026..f1ea77d74ac9979bdffccfd195ca76eabd98c3a9 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -272,9 +272,9 @@ class MailFetcher { $sender=$headerinfo->from[0]; //Just what we need... - $header=array('name' =>@$sender->personal, + $header=array('name' => $this->mime_decode(@$sender->personal), 'email' => trim(strtolower($sender->mailbox).'@'.$sender->host), - 'subject'=>@$headerinfo->subject, + 'subject'=> $this->mime_decode(@$headerinfo->subject), 'mid' => trim(@$headerinfo->message_id), 'header' => $this->getHeader($mid), 'in-reply-to' => $headerinfo->in_reply_to, @@ -286,22 +286,56 @@ class MailFetcher { $header['reply-to-name'] = $replyto[0]->personal; } - //Try to determine target email - useful when fetched inbox has - // aliases that are independent emails within osTicket. - $emailId = 0; + // Put together a list of recipients $tolist = array(); if($headerinfo->to) - $tolist = array_merge($tolist, $headerinfo->to); + $tolist['to'] = $headerinfo->to; if($headerinfo->cc) - $tolist = array_merge($tolist, $headerinfo->cc); - if($headerinfo->bcc) - $tolist = array_merge($tolist, $headerinfo->bcc); + $tolist['cc'] = $headerinfo->cc; + + //Add delivered-to address to list. + if (stripos($header['header'], 'delivered-to:') !==false + && ($dt = Mail_Parse::findHeaderEntry($header['header'], + 'delivered-to', true))) { + if (($delivered_to = Mail_Parse::parseAddressList($dt))) + $tolist['delivered-to'] = $delivered_to; + } - foreach($tolist as $addr) - if(($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) - break; + $header['recipients'] = array(); + foreach($tolist as $source => $list) { + foreach($list as $addr) { + if (!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) { + //Skip virtual Delivered-To addresses + if ($source == 'delivered-to') continue; + + $header['recipients'][] = array( + 'source' => "Email ($source)", + 'name' => $this->mime_decode(@$addr->personal), + 'email' => strtolower($addr->mailbox).'@'.$addr->host); + } elseif(!$header['emailId']) { + $header['emailId'] = $emailId; + } + } + } + + //See if any of the recipients is a delivered to address + if ($tolist['delivered-to']) { + foreach ($tolist['delivered-to'] as $addr) { + foreach ($header['recipients'] as $i => $r) { + if (strcasecmp($r['email'], $addr->mailbox.'@'.$addr->host) === 0) + $header['recipients'][$i]['source'] = 'delivered-to'; + } + } + } - $header['emailId'] = $emailId; + //BCCed? + if(!$header['emailId']) { + if ($headerinfo->bcc) { + foreach($headerinfo->bcc as $addr) + if (($header['emailId'] = Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) + break; + } + } // Ensure we have a message-id. If unable to read it out of the // email, use the hash of the entire email headers @@ -465,10 +499,8 @@ class MailFetcher { foreach ($struct->parameters as $p) { if (strtolower($p->attribute) == 'report-type' && $p->value == 'delivery-status') { - return sprintf('<pre>%s</pre>', - Format::htmlchars( - $this->getPart($mid, 'text/plain', $this->charset, $struct, false, 1) - )); + return new TextThreadBody( $this->getPart( + $mid, 'text/plain', $this->charset, $struct, false, 1)); } } } @@ -498,27 +530,23 @@ class MailFetcher { global $cfg; if ($cfg->isHtmlThreadEnabled()) { - if ($body=$this->getPart($mid, 'text/html', $this->charset)) { - //Cleanup the html. - $body = (trim($body, " <>br/\t\n\r")) - ? Format::safe_html($body) - : '--'; - } - elseif ($body=$this->getPart($mid, 'text/plain', $this->charset)) { - $body = trim($body) - ? sprintf('<pre>%s</pre>', - Format::htmlchars($body)) - : '--'; - } - } - else { - if (!($body=$this->getPart($mid, 'text/plain', $this->charset))) { - if ($body=$this->getPart($mid, 'text/html', $this->charset)) { - $body = Format::html2text(Format::safe_html($body), 100, false); - } - } - $body = trim($body) ? $body : '--'; + if ($html=$this->getPart($mid, 'text/html', $this->charset)) + $body = new HtmlThreadBody($html); + elseif ($text=$this->getPart($mid, 'text/plain', $this->charset)) + $body = new TextThreadBody($text); } + elseif ($text=$this->getPart($mid, 'text/plain', $this->charset)) + $body = new TextThreadBody($text); + elseif ($html=$this->getPart($mid, 'text/html', $this->charset)) + $body = new TextThreadBody( + Format::html2text(Format::safe_html($html), + 100, false)); + else + $body = new TextThreadBody(''); + + if ($cfg->stripQuotedReply()) + $body->stripQuotedReply($cfg->getReplySeparator()); + return $body; } @@ -575,6 +603,11 @@ class MailFetcher { } $vars = $mailinfo; + $vars['name'] = $mailinfo['name']; + $vars['subject'] = $mailinfo['subject'] ?: '[No Subject]'; + $vars['emailId'] = $mailinfo['emailId'] ?: $this->getEmailId(); + $vars['to-email-id'] = $mailinfo['emailId'] ?: 0; + if ($this->isBounceNotice($mid)) { // Fetch the original References and assign to 'references' if ($msg = $this->getOriginalMessage($mid)) { @@ -587,21 +620,14 @@ class MailFetcher { $vars['flags']['bounce'] = true; } else { + $vars['message'] = $this->getBody($mid); $vars['flags']['bounce'] = TicketFilter::isBounce($info); - $vars['message']=Format::stripEmptyLines($this->getBody($mid)); } - $vars['name']=$this->mime_decode($mailinfo['name']); - $vars['subject']=$mailinfo['subject']?$this->mime_decode($mailinfo['subject']):'[No Subject]'; - $vars['emailId']=$mailinfo['emailId']?$mailinfo['emailId']:$this->getEmailId(); //Missing FROM name - use email address. if(!$vars['name']) - $vars['name'] = $vars['email']; - - //An email with just attachments can have empty body. - if(!$vars['message']) - $vars['message'] = '-'; + list($vars['name']) = explode('@', $vars['email']); if($ost->getConfig()->useEmailPriority()) $vars['priorityId']=$this->getPriority($mid); @@ -675,6 +701,7 @@ class MailFetcher { return null; } + return $ticket; } diff --git a/include/class.mailparse.php b/include/class.mailparse.php index b3f651ffb935c877b9677c53894efa1695a05f90..0ec0b4f190217b90c633f220f6440a7c89e625ce 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -58,6 +58,7 @@ class Mail_Parse { 'include_bodies'=> $this->include_bodies, 'decode_headers'=> $this->decode_headers, 'decode_bodies' => $this->decode_bodies); + $this->struct=Mail_mimeDecode::decode($params); if (PEAR::isError($this->struct)) @@ -164,9 +165,9 @@ class Mail_Parse { } /* static */ - function findHeaderEntry($headers, $name) { + function findHeaderEntry($headers, $name, $allEntries=false) { if (!is_array($headers)) - $headers = self::splitHeaders($headers); + $headers = self::splitHeaders($headers, $allEntries); foreach ($headers as $key=>$val) if (strcasecmp($key, $name) === 0) return $val; @@ -195,21 +196,31 @@ class Mail_Parse { return Mail_Parse::parseAddressList($header); } + function getDeliveredToAddressList() { + if (!($header = $this->struct->headers['delivered-to'])) + return null; + + return Mail_Parse::parseAddressList($header); + } + function getToAddressList(){ // Delivered-to incase it was a BBC mail. - $addrs = array(); + $tolist = array(); if ($header = $this->struct->headers['to']) - $addrs = Mail_Parse::parseAddressList($header); - - if ($header = $this->struct->headers['delivered-to']) - $addrs = array_merge($addrs, + $tolist = array_merge($tolist, Mail_Parse::parseAddressList($header)); - - return $addrs; + return $tolist ? $tolist : null; } function getCcAddressList(){ - if (!($header = $this->struct->headers['cc'])) + if (!($header = @$this->struct->headers['cc'])) + return null; + + return Mail_Parse::parseAddressList($header); + } + + function getBccAddressList(){ + if (!($header = @$this->struct->headers['bcc'])) return null; return Mail_Parse::parseAddressList($header); @@ -226,7 +237,7 @@ class Mail_Parse { } function getReplyTo() { - if (!($header = $this->struct->headers['reply-to'])) + if (!($header = @$this->struct->headers['reply-to'])) return null; return Mail_Parse::parseAddressList($header); @@ -269,36 +280,32 @@ class Mail_Parse { function getBody(){ global $cfg; - if ($cfg->isHtmlThreadEnabled()) { - if ($body=$this->getPart($this->struct,'text/html')) { - // Cleanup the html -- Balance html tags & neutralize unsafe tags. - $body = (trim($body, " <>br/\t\n\r")) - ? Format::safe_html($body) - : '--'; - } - elseif ($body=$this->getPart($this->struct,'text/plain')) { - $body = trim($body) - ? sprintf('<pre>%s</pre>', - Format::htmlchars($body)) - : '--'; - } - } - else { - if (!($body=$this->getPart($this->struct,'text/plain'))) { - if ($body=$this->getPart($this->struct,'text/html')) { - $body = Format::html2text(Format::safe_html($body), 100, false); - } - } - $body = trim($body) ? $body : '--'; + if ($cfg && $cfg->isHtmlThreadEnabled()) { + if ($html=$this->getPart($this->struct,'text/html')) + $body = new HtmlThreadBody($html); + elseif ($text=$this->getPart($this->struct,'text/plain')) + $body = new TextThreadBody($text); } + elseif ($text=$this->getPart($this->struct,'text/plain')) + $body = new TextThreadBody($text); + elseif ($html=$this->getPart($this->struct,'text/html')) + $body = new TextThreadBody( + Format::html2text(Format::safe_html($html), + 100, false)); + else + $body = new TextThreadBody(''); + + if ($cfg && $cfg->stripQuotedReply()) + $body->stripQuotedReply($cfg->getReplySeparator()); + return $body; } function getPart($struct, $ctypepart, $recurse=-1) { - if($struct && !$struct->parts) { + if($struct && !@$struct->parts) { $ctype = @strtolower($struct->ctype_primary.'/'.$struct->ctype_secondary); - if ($struct->disposition + if (@$struct->disposition && (strcasecmp($struct->disposition, 'inline') !== 0)) return ''; if ($ctype && strcasecmp($ctype,$ctypepart)==0) { @@ -317,7 +324,7 @@ class Mail_Parse { return $content; $data=''; - if($struct && $struct->parts && $recurse) { + if($struct && @$struct->parts && $recurse) { foreach($struct->parts as $i=>$part) { if($part && ($text=$this->getPart($part,$ctypepart,$recurse - 1))) $data.=$text; @@ -492,6 +499,7 @@ class EmailDataParser { $data =array(); $data['emailId'] = 0; + $data['recipients'] = array(); $data['subject'] = $parser->getSubject(); $data['header'] = $parser->getHeader(); $data['mid'] = $parser->getMessageId(); @@ -517,20 +525,64 @@ class EmailDataParser { $data['name'] = $data['email']; } - //TO Address:Try to figure out the email address... associated with the incoming email. - $emailId = 0; - if(($tolist = $parser->getToAddressList())) { - foreach ($tolist as $toaddr) { - if(($emailId=Email::getIdByEmail($toaddr->mailbox.'@'.$toaddr->host))) - break; + /* Scan through the list of addressees (via To, Cc, and Delivered-To headers), and identify + * how the mail arrived at the system. One of the mails should be in the system email list. + * The recipient list (without the Delivered-To addressees) will be made available to the + * ticket filtering system. However, addresses in the Delivered-To header should never be + * considered for the collaborator list. + */ + + $tolist = array(); + if (($to = $parser->getToAddressList())) + $tolist['to'] = $to; + + if (($cc = $parser->getCcAddressList())) + $tolist['cc'] = $cc; + + if (($dt = $parser->getDeliveredToAddressList())) + $tolist['delivered-to'] = $dt; + + foreach ($tolist as $source => $list) { + foreach($list as $addr) { + if (!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) { + //Skip virtual Delivered-To addresses + if ($source == 'delivered-to') continue; + + $data['recipients'][] = array( + 'source' => "Email ($source)", + 'name' => trim(@$addr->personal, '"'), + 'email' => strtolower($addr->mailbox).'@'.$addr->host); + } elseif(!$data['emailId']) { + $data['emailId'] = $emailId; + } + } + } + + /* + * In the event that the mail was delivered to the system although none of the system + * mail addresses are in the addressee lists, be careful not to include the addressee + * in the collaborator list. Therefore, the delivered-to addressees should be flagged so they + * are not added to the collaborator list in the ticket creation process. + */ + if ($tolist['delivered-to']) { + foreach ($tolist['delivered-to'] as $addr) { + foreach ($data['recipients'] as $i=>$r) { + if (strcasecmp($r['email'], $addr->mailbox.'@'.$addr->host) === 0) + $data['recipients'][$i]['source'] = 'delivered-to'; + } } } - //maybe we got CC'ed?? - if(!$emailId && ($cclist=$parser->getCcAddressList())) { - foreach ($cclist as $ccaddr) { - if(($emailId=Email::getIdByEmail($ccaddr->mailbox.'@'.$ccaddr->host))) - break; + + + //maybe we got BCC'ed?? + if(!$data['emailId']) { + $emailId = 0; + if($bcc = $parser->getBccAddressList()) { + foreach ($bcc as $addr) + if(($emailId=Email::getIdByEmail($addr->mailbox.'@'.$addr->host))) + break; } + $data['emailId'] = $emailId; } if ($parser->isBounceNotice()) { @@ -544,13 +596,13 @@ class EmailDataParser { } else { // Typical email - $data['message'] = Format::stripEmptyLines($parser->getBody()); - $data['in-reply-to'] = $parser->struct->headers['in-reply-to']; - $data['references'] = $parser->struct->headers['references']; + $data['message'] = $parser->getBody(); + $data['in-reply-to'] = @$parser->struct->headers['in-reply-to']; + $data['references'] = @$parser->struct->headers['references']; $data['flags']['bounce'] = TicketFilter::isBounce($data['header']); } - $data['emailId'] = $emailId; + $data['to-email-id'] = $data['emailId']; if (($replyto = $parser->getReplyTo())) { $replyto = $replyto[0]; diff --git a/include/class.nav.php b/include/class.nav.php index 5a391ff553967c1338828ab6796ea079a615a6dd..1648332948ec1a32e5ac9a12aba75827d3087a15 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -213,6 +213,7 @@ class AdminNav extends StaffNav{ $subnav[]=array('desc'=>'Pages', 'href'=>'pages.php','title'=>'Pages','iconclass'=>'pages'); $subnav[]=array('desc'=>'Forms','href'=>'forms.php','iconclass'=>'forms'); $subnav[]=array('desc'=>'Lists','href'=>'lists.php','iconclass'=>'lists'); + $subnav[]=array('desc'=>'Plugins','href'=>'plugins.php','iconclass'=>'api'); break; case 'emails': $subnav[]=array('desc'=>'Emails','href'=>'emails.php', 'title'=>'Email Addresses', 'iconclass'=>'emailSettings'); @@ -281,12 +282,12 @@ class UserNav { $navs['new']=array('desc'=>'Open New Ticket','href'=>'open.php','title'=>''); if($user && $user->isValid()) { if($cfg && $cfg->showRelatedTickets()) { - $navs['tickets']=array('desc'=>sprintf('My Tickets (%d)',$user->getNumTickets()), + $navs['tickets']=array('desc'=>sprintf('Tickets (%d)',$user->getNumTickets()), 'href'=>'tickets.php', 'title'=>'Show all tickets'); } else { $navs['tickets']=array('desc'=>'View Ticket Thread', - 'href'=>sprintf('tickets.php?id=%d',$user->getTicketID()), + 'href'=>sprintf('tickets.php?id=%d',$user->getTicketId()), 'title'=>'View ticket status'); } } else { diff --git a/include/class.orm.php b/include/class.orm.php index dee287cea77adc73d323db573e79a83151a998de..b9bda6a1176ac4beea1aee9301360e50328a315b 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -162,7 +162,7 @@ class VerySimpleModel { $sql = 'UPDATE '.static::$meta['table']; $filter = $fields = array(); if (count($this->dirty) === 0) - return; + return true; foreach ($this->dirty as $field=>$old) { if ($this->__new__ or !in_array($field, $pk)) { if (@get_class($this->get($field)) == 'SqlFunction') @@ -693,6 +693,8 @@ class MySqlCompiler extends SqlCompiler { 'contains' => array('self', '__contains'), 'gt' => '%1$s > %2$s', 'lt' => '%1$s < %2$s', + 'gte' => '%1$s >= %2$s', + 'lte' => '%1$s <= %2$s', 'isnull' => '%1$s IS NULL', 'like' => '%1$s LIKE %2$s', 'in' => array('self', '__in'), diff --git a/include/class.osticket.php b/include/class.osticket.php index f27efe4a9323487b74af412582609126a94f8a21..98979448ccf100f271caa41670046301ebf55e63 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -20,6 +20,7 @@ require_once(INCLUDE_DIR.'class.csrf.php'); //CSRF token class. require_once(INCLUDE_DIR.'class.migrater.php'); +require_once(INCLUDE_DIR.'class.plugin.php'); define('LOG_WARN',LOG_WARNING); @@ -46,6 +47,7 @@ class osTicket { var $session; var $csrf; var $company; + var $plugins; function osTicket() { @@ -59,6 +61,8 @@ class osTicket { $this->csrf = new CSRF('__CSRFToken__'); $this->company = new Company(); + + $this->plugins = new PluginManager(); } function isSystemOnline() { @@ -434,6 +438,9 @@ class osTicket { $_SESSION['TZ_OFFSET'] = $ost->getConfig()->getTZoffset(); $_SESSION['TZ_DST'] = $ost->getConfig()->observeDaylightSaving(); + // Bootstrap installed plugins + $ost->plugins->bootstrap(); + return $ost; } } diff --git a/include/class.ostsession.php b/include/class.ostsession.php index e588f618525bf715b576dd1b03a174f116638ca6..beb344f9f3734f5e286e9d2e4aff0060d6406cb2 100644 --- a/include/class.ostsession.php +++ b/include/class.ostsession.php @@ -90,14 +90,14 @@ class osTicketSession { list($this->data)=db_fetch_row($res); $this->id = $id; } - $this->data_hash = md5($this->data); + $this->data_hash = md5($id.$this->data); return $this->data; } function write($id, $data){ global $thisstaff; - if (md5($data) == $this->data_hash) + if (md5($id.$data) == $this->data_hash) return; $ttl = ($this && get_class($this) == 'osTicketSession') diff --git a/include/class.pdf.php b/include/class.pdf.php index 7c5e6b775d8455d33b5489d67f685a6955dcbdb9..404e4dd7bd942ad0114a919fe0efec83d1c83a28 100644 --- a/include/class.pdf.php +++ b/include/class.pdf.php @@ -291,7 +291,7 @@ class Ticket2PDF extends mPDF $this->WriteCell($w/2, 7, Format::db_datetime($entry['created']), 'LTB', 0, 'L', true); $this->SetFont('Arial', '', 10); $this->WriteCell($w, 7, Format::truncate($entry['title'], 50), 'TB', 0, 'L', true); - $this->WriteCell($w/2, 7, $entry['poster'], 'TBR', 1, 'L', true); + $this->WriteCell($w/2, 7, $entry['name'] ?: $entry['poster'], 'TBR', 1, 'L', true); $this->SetFont(''); $text= $entry['body']; if($entry['attachments'] diff --git a/include/class.plugin.php b/include/class.plugin.php new file mode 100644 index 0000000000000000000000000000000000000000..6a8ac830b8d372b3dd9974af60ca555635e77898 --- /dev/null +++ b/include/class.plugin.php @@ -0,0 +1,414 @@ +<?php + +require_once(INCLUDE_DIR.'/class.config.php'); +class PluginConfig extends Config { + var $table = CONFIG_TABLE; + var $form; + + function __construct($name) { + // Use parent constructor to place configurable information into the + // central config table in a namespace of "plugin.<id>" + parent::Config("plugin.$name"); + } + + /* abstract */ + function getOptions() { + return array(); + } + + /** + * Retreive a Form instance for the configurable options offered in + * ::getOptions + */ + function getForm() { + if (!isset($this->form)) { + $this->form = new Form($this->getOptions()); + if ($_SERVER['REQUEST_METHOD'] != 'POST') + $this->form->data($this->getInfo()); + } + return $this->form; + } + + /** + * commit + * + * Used in the POST request of the configuration process. The + * ::getForm() method should be used to retrieve a configuration form + * for this plugin. That form should be submitted via a POST request, + * and this method should be called in that request. The data from the + * POST request will be interpreted and will adjust the configuration of + * this field + * + * Parameters: + * errors - (OUT array) receives validation errors of the parsed + * configuration form + * + * Returns: + * (bool) true if the configuration was updated, false if there were + * errors. If false, the errors were written into the received errors + * array. + */ + function commit(&$errors=array()) { + $f = $this->getForm(); + $commit = false; + if ($f->isValid()) { + $config = $f->getClean(); + $commit = $this->pre_save($config, $errors); + } + $errors += $f->errors(); + if ($commit && count($errors) === 0) + return $this->updateAll($config); + return false; + } + + /** + * Pre-save hook to check configuration for errors (other than obvious + * validation errors) prior to saving. Add an error to the errors list + * or return boolean FALSE if the config commit should be aborted. + */ + function pre_save($config, &$errors) { + return true; + } + + /** + * Remove all configuration for this plugin -- used when the plugin is + * uninstalled + */ + function purge() { + $sql = 'DELETE FROM '.$this->table + .' WHERE `namespace`='.db_input($this->getNamespace()); + return (db_query($sql) && db_affected_rows()); + } +} + +class PluginManager { + static private $plugin_info = array(); + static private $plugin_list = array(); + + /** + * boostrap + * + * Used to bootstrap the plugin subsystem and initialize all the plugins + * currently enabled. + */ + function bootstrap() { + foreach ($this->allActive() as $p) + $p->bootstrap(); + } + + /** + * allActive + * + * Scans the plugin registry to find all installed and active plugins. + * Those plugins are included, instanciated, and cached in a list. + * + * Returns: + * Array<Plugin> a cached list of instanciated plugins for all installed + * and active plugins + */ + static function allInstalled() { + if (static::$plugin_list) + return static::$plugin_list; + + $sql = 'SELECT * FROM '.PLUGIN_TABLE; + if (!($res = db_query($sql))) + return static::$plugin_list; + + while ($ht = db_fetch_array($res)) { + // XXX: Only read active plugins here. allInfos() will + // read all plugins + $info = static::getInfoForPath( + INCLUDE_DIR . $ht['install_path'], $ht['isphar']); + list($path, $class) = explode(':', $info['plugin']); + if (!$class) + $class = $path; + elseif ($ht['isphar']) + require_once('phar://' . INCLUDE_DIR . $ht['install_path'] + . '/' . $path); + else + require_once(INCLUDE_DIR . $ht['install_path'] + . '/' . $path); + if ($ht['isactive']) { + static::$plugin_list[$ht['install_path']] + = new $class($ht['id']); + } + else { + // Get instance without calling the constructor. Thanks + // http://stackoverflow.com/a/2556089 + $a = unserialize( + sprintf( + 'O:%d:"%s":0:{}', + strlen($class), $class + ) + ); + // Simulate __construct() and load() + $a->id = $ht['id']; + $a->ht = $ht; + $a->info = $info; + static::$plugin_list[$ht['install_path']] = &$a; + unset($a); + } + } + return static::$plugin_list; + } + + static function allActive() { + $plugins = array(); + foreach (static::allInstalled() as $p) + if ($p instanceof Plugin && $p->isActive()) + $plugins[] = $p; + return $plugins; + } + + function throwException($errno, $errstr) { + throw new RuntimeException($errstr); + } + + /** + * allInfos + * + * Scans the plugin folders for installed plugins. For each one, the + * plugin.php file is included and the info array returned in added to + * the list returned. + * + * Returns: + * Information about all available plugins. The registry will have to be + * queried to determine if the plugin is installed + */ + static function allInfos() { + foreach (glob(INCLUDE_DIR . 'plugins/*', + GLOB_NOSORT|GLOB_BRACE) as $p) { + $is_phar = false; + if (substr($p, strlen($p) - 5) == '.phar' + && Phar::isValidPharFilename($p)) { + try { + // When public key is invalid, openssl throws a + // 'supplied key param cannot be coerced into a public key' warning + // and phar ignores sig verification. + // We need to protect from that by catching the warning + // Thanks, https://github.com/koto/phar-util + set_error_handler(array('self', 'throwException')); + $ph = new Phar($p); + restore_error_handler(); + // Verify the signature + $ph->getSignature(); + $p = 'phar://' . $p; + $is_phar = true; + } catch (UnexpectedValueException $e) { + // Cannot find signature file + } catch (RuntimeException $e) { + // Invalid signature file + } + + } + + if (!is_file($p . '/plugin.php')) + // Invalid plugin -- must define "/plugin.php" + continue; + + // Cache the info into static::$plugin_info + static::getInfoForPath($p, $is_phar); + } + return static::$plugin_info; + } + + static function getInfoForPath($path, $is_phar=false) { + static $defaults = array( + 'include' => 'include/', + 'stream' => false, + ); + + $install_path = str_replace(INCLUDE_DIR, '', $path); + $install_path = str_replace('phar://', '', $install_path); + if ($is_phar && substr($path, 0, 7) != 'phar://') + $path = 'phar://' . $path; + if (!isset(static::$plugin_info[$install_path])) { + // plugin.php is require to return an array of informaiton about + // the plugin. + $info = array_merge($defaults, (include $path . '/plugin.php')); + $info['install_path'] = $install_path; + + // XXX: Ensure 'id' key isset + static::$plugin_info[$install_path] = $info; + } + return static::$plugin_info[$install_path]; + } + + function getInstance($path) { + static $instances = array(); + if (!isset($instances[$path]) + && ($ps = static::allInstalled()) + && ($ht = $ps[$path]) + && ($info = static::getInfoForPath($path))) { + // $ht may be the plugin instance + if ($ht instanceof Plugin) + return $ht; + // Usually this happens when the plugin is being enabled + list($path, $class) = explode(':', $info['plugin']); + if (!$class) + $class = $path; + else + require_once(INCLUDE_DIR . $info['install_path'] . '/' . $path); + $instances[$path] = new $class($ht['id']); + } + return $instances[$path]; + } + + /** + * install + * + * Used to install a plugin that is in-place on the filesystem, but not + * registered in the plugin registry -- the %plugin table. + */ + function install($path) { + $is_phar = substr($path, strlen($path) - 5) == '.phar'; + if (!($info = $this->getInfoForPath(INCLUDE_DIR . $path, $is_phar))) + return false; + + $sql='INSERT INTO '.PLUGIN_TABLE.' SET installed=NOW() ' + .', install_path='.db_input($path) + .', name='.db_input($info['name']) + .', isphar='.db_input($is_phar); + if (!db_query($sql) || !db_affected_rows()) + return false; + static::clearCache(); + return true; + } + + static function clearCache() { + static::$plugin_list = array(); + } +} + +/** + * Class: Plugin (abstract) + * + * Base class for plugins. Plugins should inherit from this class and define + * the useful pieces of the + */ +abstract class Plugin { + /** + * Configuration manager for the plugin. Should be the name of a class + * that inherits from PluginConfig. This is abstract and must be defined + * by the plugin subclass. + */ + var $config_class = null; + var $id; + var $info; + + function Plugin($id) { + $this->id = $id; + $this->load(); + } + + function load() { + $sql = 'SELECT * FROM '.PLUGIN_TABLE.' WHERE + `id`='.db_input($this->id); + if (($res = db_query($sql)) && ($ht=db_fetch_array($res))) + $this->ht = $ht; + $this->info = PluginManager::getInfoForPath($this->ht['install_path'], + $this->isPhar()); + } + + function getId() { return $this->id; } + function getName() { return $this->info['name']; } + function isActive() { return $this->ht['isactive']; } + function isPhar() { return $this->ht['isphar']; } + function getInstallDate() { return $this->ht['installed']; } + + function getIncludePath() { + return realpath(INCLUDE_DIR . $this->info['install_path'] . '/' + . $this->info['include_path']) . '/'; + } + + /** + * Main interface for plugins. Called at the beginning of every request + * for each installed plugin. Plugins should register functionality and + * connect to signals, etc. + */ + abstract function bootstrap(); + + /** + * uninstall + * + * Removes the plugin from the plugin registry. The files remain on the + * filesystem which would allow the plugin to be reinstalled. The + * configuration for the plugin is also removed. If the plugin is + * reinstalled, it will have to be reconfigured. + */ + function uninstall(&$errors) { + if ($this->pre_uninstall($errors) === false) + return false; + + $sql = 'DELETE FROM '.PLUGIN_TABLE + .' WHERE id='.db_input($this->getId()); + PluginManager::clearCache(); + if (!db_query($sql) || !db_affected_rows()) + return false; + + $this->getConfig()->purge(); + return true; + } + + /** + * pre_uninstall + * + * Hook function to veto the uninstallation request. Return boolean + * FALSE if the uninstall operation should be aborted. + */ + function pre_uninstall(&$errors) { + return true; + } + + function enable() { + $sql = 'UPDATE '.PLUGIN_TABLE + .' SET isactive=1 WHERE id='.db_input($this->getId()); + PluginManager::clearCache(); + return (db_query($sql) && db_affected_rows()); + } + + function disable() { + $sql = 'UPDATE '.PLUGIN_TABLE + .' SET isactive=0 WHERE id='.db_input($this->getId()); + PluginManager::clearCache(); + return (db_query($sql) && db_affected_rows()); + } + + /** + * upgrade + * + * Upgrade the plugin. This is used to migrate the database pieces of + * the plugin using the database migration stream packaged with the + * plugin. + */ + function upgrade() { + } + + function getConfig() { + static $config = null; + if ($config === null && $this->config_class) + $config = new $this->config_class($this->getId()); + + return $config; + } + + function source($what) { + $what = str_replace('\\', '/', $what); + if ($what && $what[0] != '/') + $what = $this->getIncludePath() . $what; + include_once $what; + } + + static function lookup($id) { //Assuming local ID is the only lookup used! + $path = false; + if ($id && is_numeric($id)) { + $sql = 'SELECT install_path FROM '.PLUGIN_TABLE + .' WHERE id='.db_input($id); + $path = db_result(db_query($sql)); + } + if ($path) + return PluginManager::getInstance($path); + } +} + +?> diff --git a/include/class.signal.php b/include/class.signal.php index 928c15c4d2392ae767a0c465b6730d859f3dc85f..424ccccc9665a36db41bb5a1ff271150d946ec82 100644 --- a/include/class.signal.php +++ b/include/class.signal.php @@ -25,6 +25,8 @@ * the codebase there exists a Signal::send() for the same named signal. */ class Signal { + static private $subscribers = array(); + /** * Subscribe to a signal. * @@ -51,10 +53,9 @@ class Signal { * signal handler. The function will receive the signal data and should * return true if the signal handler should be called. */ - /*static*/ function connect($signal, $callable, $object=null, + static function connect($signal, $callable, $object=null, $check=null) { - global $_subscribers; - if (!isset($_subscribers[$signal])) $_subscribers[$signal] = array(); + if (!isset(self::$subscribers[$signal])) self::$subscribers[$signal] = array(); // XXX: Ensure $object if set is a class if ($object && !is_string($object)) trigger_error("Invalid object: $object: Expected class"); @@ -62,7 +63,7 @@ class Signal { trigger_error("Invalid check function: Must be callable"); $check = null; } - $_subscribers[$signal][] = array($object, $callable, $check); + self::$subscribers[$signal][] = array($object, $callable, $check); } /** @@ -85,11 +86,10 @@ class Signal { * possible to propogate changes in the signal handlers back to the * originating context. */ - /*static*/ function send($signal, $object, &$data=null) { - global $_subscribers; - if (!isset($_subscribers[$signal])) + static function send($signal, $object, &$data=null) { + if (!isset(self::$subscribers[$signal])) return; - foreach ($_subscribers[$signal] as $sub) { + foreach (self::$subscribers[$signal] as $sub) { list($s, $callable, $check) = $sub; if ($s && !is_a($object, $s)) continue; @@ -99,6 +99,4 @@ class Signal { } } } - -$_subscribers = array(); ?> diff --git a/include/class.staff.php b/include/class.staff.php index 34a35e00853f54770fbc58c166460a48f171d6c9..819c68d2814dc4c834255d891dd765d361ddbffd 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -20,8 +20,9 @@ include_once(INCLUDE_DIR.'class.team.php'); include_once(INCLUDE_DIR.'class.group.php'); include_once(INCLUDE_DIR.'class.passwd.php'); include_once(INCLUDE_DIR.'class.user.php'); +include_once(INCLUDE_DIR.'class.auth.php'); -class Staff { +class Staff extends AuthenticatedUser { var $ht; var $id; @@ -66,6 +67,7 @@ class Staff { $this->teams = $this->ht['teams'] = array(); $this->group = $this->dept = null; $this->departments = $this->stats = array(); + $this->config = new Config('staff.'.$this->id); //WE have to patch info here to support upgrading from old versions. if(($time=strtotime($this->ht['passwdreset']?$this->ht['passwdreset']:$this->ht['added']))) @@ -83,8 +85,12 @@ class Staff { return $this->load(); } + function __toString() { + return (string) $this->getName(); + } + function asVar() { - return $this->getName(); + return $this->__toString(); } function getHastable() { @@ -92,7 +98,18 @@ class Staff { } function getInfo() { - return $this->getHastable(); + return $this->config->getInfo() + $this->getHastable(); + } + + // AuthenticatedUser implementation... + // TODO: Move to an abstract class that extends Staff + function getRole() { + return 'staff'; + } + + function getAuthBackend() { + list($authkey, ) = explode(':', $this->getAuthKey()); + return StaffAuthenticationBackend::getBackend($authkey); } /*compares user password*/ @@ -120,6 +137,10 @@ class Staff { return $this->check_passwd($password, false); } + function hasPassword() { + return (bool) $this->ht['passwd']; + } + function forcePasswdRest() { return db_query('UPDATE '.STAFF_TABLE.' SET change_passwd=1 WHERE staff_id='.db_input($this->getId())); } @@ -256,6 +277,17 @@ class Staff { return $this->dept; } + function getLanguage() { + static $cached = false; + if (!$cached) $cached = &$_SESSION['staff:lang']; + + if (!$cached) { + $cached = $this->config->get('lang'); + if (!$cached) + $cached = Internationalization::getDefaultLanguage(); + } + return $cached; + } function isManager() { return (($dept=$this->getDept()) && $dept->getManagerId()==$this->getId()); @@ -467,6 +499,9 @@ class Staff { if($errors) return false; + $this->config->set('lang', $vars['lang']); + $_SESSION['staff:lang'] = null; + $sql='UPDATE '.STAFF_TABLE.' SET updated=NOW() ' .' ,firstname='.db_input($vars['firstname']) .' ,lastname='.db_input($vars['lastname']) @@ -534,16 +569,23 @@ class Staff { function delete() { global $thisstaff; - if(!$thisstaff || !($id=$this->getId()) || $id==$thisstaff->getId()) + if (!$thisstaff || $this->getId() == $thisstaff->getId()) return 0; - $sql='DELETE FROM '.STAFF_TABLE.' WHERE staff_id='.db_input($id).' LIMIT 1'; + $sql='DELETE FROM '.STAFF_TABLE + .' WHERE staff_id = '.db_input($this->getId()).' LIMIT 1'; if(db_query($sql) && ($num=db_affected_rows())) { // DO SOME HOUSE CLEANING //Move remove any ticket assignments...TODO: send alert to Dept. manager? - db_query('UPDATE '.TICKET_TABLE.' SET staff_id=0 WHERE status=\'open\' AND staff_id='.db_input($id)); + db_query('UPDATE '.TICKET_TABLE.' SET staff_id=0 WHERE staff_id='.db_input($this->getId())); + + //Update the poster and clear staff_id on ticket thread table. + db_query('UPDATE '.TICKET_THREAD_TABLE + .' SET staff_id=0, poster= '.db_input($this->getName()->getOriginal()) + .' WHERE staff_id='.db_input($this->getId())); + //Cleanup Team membership table. - db_query('DELETE FROM '.TEAM_MEMBER_TABLE.' WHERE staff_id='.db_input($id)); + db_query('DELETE FROM '.TEAM_MEMBER_TABLE.' WHERE staff_id='.db_input($this->getId())); } Signal::send('model.deleted', $this); @@ -778,13 +820,17 @@ class Staff { $errors['mobile']='Valid number required'; if($vars['passwd1'] || $vars['passwd2'] || !$id) { - if(!$vars['passwd1'] && !$id) { + if($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2'])) { + $errors['passwd2']='Password(s) do not match'; + } + elseif ($vars['backend'] != 'local') { + // Password can be omitted + } + elseif(!$vars['passwd1'] && !$id) { $errors['passwd1']='Temp. password required'; $errors['temppasswd']='Required'; } elseif($vars['passwd1'] && strlen($vars['passwd1'])<6) { $errors['passwd1']='Must be at least 6 characters'; - } elseif($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2'])) { - $errors['passwd2']='Password(s) do not match'; } } @@ -814,6 +860,7 @@ class Staff { .' ,firstname='.db_input($vars['firstname']) .' ,lastname='.db_input($vars['lastname']) .' ,email='.db_input($vars['email']) + .' ,backend='.db_input($vars['backend']) .' ,phone="'.db_input(Format::phone($vars['phone']),false).'"' .' ,phone_ext='.db_input($vars['phone_ext']) .' ,mobile="'.db_input(Format::phone($vars['mobile']),false).'"' @@ -822,10 +869,12 @@ class Staff { if($vars['passwd1']) { $sql.=' ,passwd='.db_input(Passwd::hash($vars['passwd1'])); - } - if(isset($vars['change_passwd'])) - $sql.=' ,change_passwd=1'; + if(isset($vars['change_passwd'])) + $sql.=' ,change_passwd=1'; + } + elseif (!isset($vars['change_passwd'])) + $sql .= ' ,change_passwd=0'; if($id) { $sql='UPDATE '.STAFF_TABLE.' '.$sql.' WHERE staff_id='.db_input($id); diff --git a/include/class.template.php b/include/class.template.php index 9ffe6cd75a306281346c2e5175d6ea73b9a8fba5..88f3e5c5701383cca67b68f55764d0f30eb92262 100644 --- a/include/class.template.php +++ b/include/class.template.php @@ -51,6 +51,10 @@ class EmailTemplateGroup { 'group'=>'ticket.user', 'name'=>'Response/Reply Template', 'desc'=>'Template used on ticket response/reply'), + 'ticket.activity.notice'=>array( + 'group'=>'ticket.user', + 'name'=>'New Activity Notice', + 'desc'=>'Template used to notify collaborators on ticket activity (e.g CC on reply)'), 'ticket.alert'=>array( 'group'=>'ticket.staff', 'name'=>'New Ticket Alert', @@ -78,8 +82,11 @@ class EmailTemplateGroup { 'staff.pwreset' => array( 'group'=>'sys', 'name' => 'Staff Password Reset', - 'desc' => 'Notice sent to staff with the password reset link.', - 'default' => 'templates/staff.pwreset.txt'), + 'desc' => 'Notice sent to staff with the password reset link.'), + 'user.accesslink' => array( + 'group'=>'sys', + 'name' => 'User Access Link Recovery', + 'desc' => 'Notice sent to user on request with ticket access link.'), ); function EmailTemplateGroup($id){ @@ -133,7 +140,7 @@ class EmailTemplateGroup { } function getLanguage() { - return 'en_US'; + return $this->ht['lang']; } function isInUse(){ @@ -224,6 +231,10 @@ class EmailTemplateGroup { return $this->getMsgTemplate('ticket.reply'); } + function getActivityNoticeMsgTemplate() { + return $this->getMsgTemplate('ticket.activity.notice'); + } + function getOverlimitMsgTemplate() { return $this->getMsgTemplate('ticket.overlimit'); } @@ -331,6 +342,10 @@ class EmailTemplateGroup { .' ,isactive='.db_input($vars['isactive']) .' ,notes='.db_input(Format::sanitize($vars['notes'])); + if ($vars['lang_id']) + // TODO: Validation of lang_id + $sql .= ',lang='.db_input($vars['lang_id']); + if($id) { $sql='UPDATE '.EMAIL_TEMPLATE_GRP_TABLE.' SET '.$sql.' WHERE tpl_id='.db_input($id); if(db_query($sql)) diff --git a/include/class.thread.php b/include/class.thread.php index 5191b716ed3245b93eb1d2a79710540d3a4fda60..0722c80d2027ca5691afe4dc37e805911f86658b 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -113,13 +113,20 @@ class Thread { if(!$order || !in_array($order, array('DESC','ASC'))) $order='ASC'; - $sql='SELECT thread.* ' + $sql='SELECT thread.* + , COALESCE(user.name, + IF(staff.staff_id, + CONCAT_WS(" ", staff.firstname, staff.lastname), + NULL)) as name ' .' ,count(DISTINCT attach.attach_id) as attachments ' .' FROM '.TICKET_THREAD_TABLE.' thread ' + .' LEFT JOIN '.USER_TABLE.' user + ON (thread.user_id=user.id) ' + .' LEFT JOIN '.STAFF_TABLE.' staff + ON (thread.staff_id=staff.staff_id) ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (thread.ticket_id=attach.ticket_id - AND thread.id=attach.ref_id - AND thread.thread_type=attach.ref_type) ' + AND thread.id=attach.ref_id) ' .' WHERE thread.ticket_id='.db_input($this->getTicketId()); if($type && is_array($type)) @@ -161,6 +168,7 @@ class Thread { function addResponse($vars, &$errors) { $vars['ticketId'] = $this->getTicketId(); + $vars['userId'] = 0; return Response::create($vars, $errors); } @@ -178,13 +186,11 @@ class Thread { function delete() { - /* XXX: Leave this out until TICKET_EMAIL_INFO_TABLE has a primary - * key - $sql = 'DELETE mid.* FROM '.TICKET_EMAIL_INFO_TABLE.' mid - INNER JOIN '.TICKET_THREAD_TABLE.' thread ON (thread.id = mid.message_id) - WHERE thread.ticket_id = '.db_input($this->getTicketId()); + $sql = 'UPDATE '.TICKET_EMAIL_INFO_TABLE.' mid + INNER JOIN '.TICKET_THREAD_TABLE.' thread ON (thread.id = mid.thread_id) + SET mid.headers = null WHERE thread.ticket_id = ' + .db_input($this->getTicketId()); db_query($sql); - */ $res=db_query('DELETE FROM '.TICKET_THREAD_TABLE.' WHERE ticket_id='.db_input($this->getTicketId())); if(!$res || !db_affected_rows()) @@ -244,11 +250,10 @@ Class ThreadEntry { .' ,count(DISTINCT attach.attach_id) as attachments ' .' FROM '.TICKET_THREAD_TABLE.' thread ' .' LEFT JOIN '.TICKET_EMAIL_INFO_TABLE.' info - ON (thread.id=info.message_id) ' + ON (thread.id=info.thread_id) ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (thread.ticket_id=attach.ticket_id - AND thread.id=attach.ref_id - AND thread.thread_type=attach.ref_type) ' + AND thread.id=attach.ref_id) ' .' WHERE thread.id='.db_input($id); if($type) @@ -352,6 +357,37 @@ Class ThreadEntry { return $this->_references; } + function getTaggedEmailReferences($prefix, $refId) { + + $ref = "+$prefix".Base32::encode(pack('VV', $this->getId(), $refId)); + + $mid = substr_replace($this->getEmailMessageId(), + $ref, strpos($this->getEmailMessageId(), '@'), 0); + + return sprintf('%s %s', $this->getEmailReferences(), $mid); + } + + function getEmailReferencesForUser($user) { + return $this->getTaggedEmailReferences('u', + ($user instanceof Collaborator) + ? $user->getUserId() + : $user->getId()); + } + + function getEmailReferencesForStaff($staff) { + return $this->getTaggedEmailReferences('s', $staff->getId()); + } + + function getUIDFromEmailReference($ref) { + + $info = unpack('Vtid/Vuid', + Base32::decode(strtolower(substr($ref, -13)))); + + if ($info && $info['tid'] == $this->getId()) + return $info['uid']; + + } + function getTicket() { if(!$this->ticket && $this->getTicketId()) @@ -372,6 +408,18 @@ Class ThreadEntry { return $this->staff; } + function getUserId() { + return $this->ht['user_id']; + } + + function getUser() { + + if (!isset($this->user)) + $this->user = User::lookup($this->getUserId()); + + return $this->user; + } + function getEmailHeader() { return $this->ht['headers']; } @@ -433,13 +481,13 @@ Class ThreadEntry { return $uploaded; } - function importAttachments($attachments) { + function importAttachments(&$attachments) { if(!$attachments || !is_array($attachments)) return null; $files = array(); - foreach($attachments as $attachment) + foreach($attachments as &$attachment) if(($id=$this->importAttachment($attachment))) $files[] = $id; @@ -447,7 +495,7 @@ Class ThreadEntry { } /* Emailed & API attachments handler */ - function importAttachment($attachment) { + function importAttachment(&$attachment) { if(!$attachment || !is_array($attachment)) return null; @@ -469,7 +517,7 @@ Class ThreadEntry { Save attachment to the DB. @file is a mixed var - can be ID or file hashtable. */ - function saveAttachment($file) { + function saveAttachment(&$file) { if(!($fileId=is_numeric($file)?$file:AttachmentFile::save($file))) return 0; @@ -484,8 +532,7 @@ Class ThreadEntry { $sql ='INSERT IGNORE INTO '.TICKET_ATTACHMENT_TABLE.' SET created=NOW() ' .' ,file_id='.db_input($fileId) .' ,ticket_id='.db_input($this->getTicketId()) - .' ,ref_id='.db_input($this->getId()) - .' ,ref_type='.db_input($this->getType()); + .' ,ref_id='.db_input($this->getId()); return (db_query($sql) && ($id=db_insert_id()))?$id:0; } @@ -505,12 +552,11 @@ Class ThreadEntry { return $this->attachments; //XXX: inner join the file table instead? - $sql='SELECT a.attach_id, f.id as file_id, f.size, f.hash as file_hash, f.name ' + $sql='SELECT a.attach_id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.name ' .' FROM '.FILE_TABLE.' f ' .' INNER JOIN '.TICKET_ATTACHMENT_TABLE.' a ON(f.id=a.file_id) ' .' WHERE a.ticket_id='.db_input($this->getTicketId()) - .' AND a.ref_id='.db_input($this->getId()) - .' AND a.ref_type='.db_input($this->getType()); + .' AND a.ref_id='.db_input($this->getId()); $this->attachments = array(); if(($res=db_query($sql)) && db_num_rows($res)) { @@ -565,6 +611,8 @@ Class ThreadEntry { * - body - (string) email message body (decoded) */ function postEmail($mailinfo) { + global $ost; + // +==================+===================+=============+ // | Orig Thread-Type | Reply Thread-Type | Requires | // +==================+===================+=============+ @@ -583,6 +631,28 @@ Class ThreadEntry { // Reporting success so the email can be moved or deleted. return true; + // Mail sent by this system will have a message-id format of + // <code-random-mailbox@domain.tld> + // where code is a predictable string based on the SECRET_SALT of + // this osTicket installation. If this incoming mail matches the + // code, then it very likely originated from this system and looped + @list($code) = explode('-', $mailinfo['mid'], 2); + if (0 === strcasecmp(ltrim($code, '<'), substr(md5('mail'.SECRET_SALT), -9))) { + // This mail was sent by this system. It was received due to + // some kind of mail delivery loop. It should not be considered + // a response to an existing thread entry + if ($ost) $ost->log(LOG_ERR, 'Email loop detected', sprintf( + 'It appears as though <%s> is being used as a forwarded or + fetched email account and is also being used as a user / + system account. Please correct the loop or seek technical + assistance.', $mailinfo['email']), + // This is quite intentional -- don't continue the loop + false, + // Force the message, even if logging is disabled + true); + return true; + } + $vars = array( 'mid' => $mailinfo['mid'], 'header' => $mailinfo['header'], @@ -592,6 +662,8 @@ Class ThreadEntry { 'source' => 'Email', 'ip' => '', 'reply_to' => $this, + 'recipients' => $mailinfo['recipients'], + 'to-email-id' => $mailinfo['to-email-id'], ); $errors = array(); @@ -603,13 +675,17 @@ Class ThreadEntry { // Disambiguate if the user happens also to be a staff member of the // system. The current ticket owner should _always_ post messages // instead of notes or responses - if (strcasecmp($mailinfo['email'], $ticket->getEmail()) == 0) { + if ($mailinfo['userId'] + || strcasecmp($mailinfo['email'], $ticket->getEmail()) == 0) { $vars['message'] = $body; + $vars['userId'] = $mailinfo['userId'] ? $mailinfo['userId'] : $ticket->getUserId(); return $ticket->postMessage($vars, 'Email'); } - elseif ($staff_id = Staff::getIdByEmail($mailinfo['email'])) { - $vars['staffId'] = $staff_id; - $poster = Staff::lookup($staff_id); + // XXX: Consider collaborator role + elseif ($mailinfo['staffId'] + || ($mailinfo['staffId'] = Staff::getIdByEmail($mailinfo['email']))) { + $vars['staffId'] = $mailinfo['staffId']; + $poster = Staff::lookup($mailinfo['staffId']); $vars['note'] = $body; return $ticket->postNote($vars, $errors, $poster); } @@ -628,8 +704,11 @@ Class ThreadEntry { } // TODO: Consider security constraints else { + //XXX: Are we potentially leaking the email address to + // collaborators? $vars['message'] = sprintf("Received From: %s\n\n%s", $mailinfo['email'], $body); + $vars['userId'] = 0; //Unknown user! //XXX: Assume ticket owner? return $ticket->postMessage($vars, 'Email'); } // Currently impossible, but indicate that this thread object could @@ -668,7 +747,7 @@ Class ThreadEntry { /* static */ function logEmailHeaders($id, $mid, $header=false) { $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE - .' SET message_id='.db_input($id) //TODO: change it to thread_id + .' SET thread_id='.db_input($id) .', email_mid='.db_input($mid); //TODO: change it to message_id. if ($header) $sql .= ', headers='.db_input($header); @@ -677,10 +756,14 @@ Class ThreadEntry { /* variables */ - function asVar() { + function __toString() { return $this->getBody(); } + function asVar() { + return (string) $this; + } + function getVar($tag) { global $cfg; @@ -730,11 +813,11 @@ Class ThreadEntry { * previously seen. This is useful if no thread-id is associated * with the email (if it was rejected for instance). */ - function lookupByEmailHeaders($mailinfo, &$seen=false) { + function lookupByEmailHeaders(&$mailinfo, &$seen=false) { // Search for messages using the References header, then the // in-reply-to header - $search = 'SELECT message_id, email_mid FROM '.TICKET_EMAIL_INFO_TABLE - . ' WHERE email_mid=%s ORDER BY message_id DESC'; + $search = 'SELECT thread_id, email_mid FROM '.TICKET_EMAIL_INFO_TABLE + . ' WHERE email_mid=%s ORDER BY thread_id DESC'; if (list($id, $mid) = db_fetch_row(db_query( sprintf($search, db_input($mailinfo['mid']))))) { @@ -757,10 +840,27 @@ Class ThreadEntry { // @see rfc 1036, section 2.2.5 // @see http://www.jwz.org/doc/threading.html foreach (array_reverse($matches[0]) as $mid) { + //Try to determine if it's a reply to a tagged email. + $ref = null; + if (strpos($mid, '+')) { + list($left, $right) = explode('@',$mid); + list($left, $ref) = explode('+', $left); + $mid = "$left@$right"; + } $res = db_query(sprintf($search, db_input($mid))); while (list($id) = db_fetch_row($res)) { - if ($t = ThreadEntry::lookup($id)) - return $t; + if (!($t = ThreadEntry::lookup($id))) continue; + + //We found a match - see if we can ID the user. + // XXX: Check access of ref is enough? + if ($ref && ($uid = $t->getUIDFromEmailReference($ref))) { + if ($ref[0] =='s') //staff + $mailinfo['staffId'] = $uid; + else //user or collaborator. + $mailinfo['userId'] = $uid; + } + + return $t; } } } @@ -770,16 +870,85 @@ Class ThreadEntry { // injection by third-party. $subject = $mailinfo['subject']; $match = array(); - if ($subject && $mailinfo['email'] + if ($subject + && $mailinfo['email'] && preg_match("/#(?:[\p{L}-]+)?([0-9]{1,10})/u", $subject, $match) - && ($tid = Ticket::getIdByExtId((int)$match[1], $mailinfo['email'])) - ) - // Return last message for the thread - return Message::lastByTicketId($tid); + //Lookup by ticket number + && ($ticket = Ticket::lookupByNumber((int)$match[1])) + //Lookup the user using the email address + && ($user = User::lookup(array('emails__address' => $mailinfo['email'])))) { + //We have a valid ticket and user + if ($ticket->getUserId() == $user->getId() //owner + || ($c = Collaborator::lookup( // check if collaborator + array('userId' => $user->getId(), + 'ticketId' => $ticket->getId())))) { + + $mailinfo['userId'] = $user->getId(); + return $ticket->getLastMessage(); + } + } + + // Search for the message-id token in the body + if (preg_match('`(?:data-mid="|Ref-Mid: )([^"\s]*)(?:$|")`', + $mailinfo['message'], $match)) + if ($thread = ThreadEntry::lookupByMessageId($match[1])) + return $thread; return null; } + /** + * Find a thread entry from a message-id created from the + * ::asMessageId() method + */ + function lookupByMessageId($mid, $from) { + $mid = trim($mid, '<>'); + list($ver, $ids, $mails) = explode('$', $mid, 3); + + // Current version is <null> + if ($ver !== '') + return false; + + $ids = @unpack('Vthread', base64_decode($ids)); + if (!$ids || !$ids['thread']) + return false; + + $thread = ThreadEntry::lookup($ids['thread']); + if (!$thread) + return false; + + if (0 === strcasecmp($thread->asMessageId($from, $ver), $mid)) + return $thread; + } + + /** + * Get an email message-id that can be used to represent this thread + * entry. The same message-id can be passed to ::lookupByMessageId() to + * find this thread entry + * + * Formats: + * Initial (version <null>) + * <$:b32(thread-id)$:md5(to-addr.ticket-num.ticket-id)@:md5(url)> + * thread-id - thread-id, little-endian INT, packed + * :b32() - base32 encoded + * to-addr - individual email recipient + * ticket-num - external ticket number + * ticket-id - internal ticket id + * :md5() - last 10 hex chars of MD5 sum + * url - helpdesk URL + */ + function asMessageId($to, $version=false) { + global $ost; + + $domain = md5($ost->getConfig()->getURL()); + $ticket = $this->getTicket(); + return sprintf('$%s$%s@%s', + base64_encode(pack('V', $this->getId())), + substr(md5($to . $ticket->getNumber() . $ticket->getId()), -10), + substr($domain, -10) + ); + } + //new entry ... we're trusting the caller to check validity of the data. function create($vars) { global $cfg; @@ -788,39 +957,50 @@ Class ThreadEntry { if(!$vars['ticketId'] || !$vars['type'] || !in_array($vars['type'], array('M','R','N'))) return false; - if (isset($vars['attachments'])) { - foreach ($vars['attachments'] as &$a) { - // Change <img src="cid:"> inside the message to point to - // a unique hash-code for the attachment. Since the - // content-id will be discarded, only the unique hash-code - // will be available to retrieve the image later - if ($a['cid']) { - $a['hash'] = Misc::randCode(32); - $vars['body'] = str_replace('src="cid:'.$a['cid'].'"', - 'src="cid:'.$a['hash'].'"', $vars['body']); + + if (!$vars['body'] instanceof ThreadBody) { + if ($cfg->isHtmlThreadEnabled()) + $vars['body'] = new HtmlThreadBody($vars['body']); + else + $vars['body'] = new TextThreadBody($vars['body']); + } + + // Drop stripped images. NOTE: This should be done before + // ->convert() because the strippedImages list will not propagate to + // the newly converted thread body + if ($vars['attachments']) { + foreach ($vars['body']->getStrippedImages() as $cid) { + foreach ($vars['attachments'] as $i=>$a) { + if (@$a['cid'] && $a['cid'] == $cid) { + // Inline referenced attachment was stripped + unset($vars['attachments']); + } } } - unset($a); } - if (!$cfg->isHtmlThreadEnabled()) { - // Data in the database is assumed to be HTML, change special - // plain text XML characters - $vars['title'] = Format::htmlchars($vars['title']); - $vars['body'] = sprintf('<pre>%s</pre>', - Format::htmlchars($vars['body'])); - } - $vars['body'] = Format::sanitize($vars['body']); + if (!($body = Format::sanitize( + (string) $vars['body']->convertTo('html')))) + $body = '-'; //Special tag used to signify empty message as stored. + + $poster = $vars['poster']; + if ($poster && is_object($poster)) + $poster = (string) $poster; $sql=' INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW() ' .' ,thread_type='.db_input($vars['type']) .' ,ticket_id='.db_input($vars['ticketId']) .' ,title='.db_input(Format::sanitize($vars['title'], true)) - .' ,body='.db_input($vars['body']) .' ,staff_id='.db_input($vars['staffId']) - .' ,poster='.db_input($vars['poster']) + .' ,user_id='.db_input($vars['userId']) + .' ,poster='.db_input($poster) .' ,source='.db_input($vars['source']); + if (!isset($vars['attachments']) || !$vars['attachments']) + // Otherwise, body will be configured in a block below (after + // inline attachments are saved and updated in the database) + $sql.=' ,body='.db_input($body); + if(isset($vars['pid'])) $sql.=' ,pid='.db_input($vars['pid']); // Check if 'reply_to' is in the $vars as the previous ThreadEntry @@ -843,14 +1023,29 @@ Class ThreadEntry { if($vars['files']) //expects well formatted and VALIDATED files array. $entry->uploadFiles($vars['files']); - //Emailed or API attachments - if($vars['attachments']) - $entry->importAttachments($vars['attachments']); - //Canned attachments... if($vars['cannedattachments'] && is_array($vars['cannedattachments'])) $entry->saveAttachments($vars['cannedattachments']); + //Emailed or API attachments + if (isset($vars['attachments']) && $vars['attachments']) { + $entry->importAttachments($vars['attachments']); + foreach ($vars['attachments'] as $a) { + // Change <img src="cid:"> inside the message to point to + // a unique hash-code for the attachment. Since the + // content-id will be discarded, only the unique hash-code + // will be available to retrieve the image later + if ($a['cid'] && $a['key']) { + $body = str_replace('src="cid:'.$a['cid'].'"', + 'src="cid:'.$a['key'].'"', $body); + } + } + $sql = 'UPDATE '.TICKET_THREAD_TABLE.' SET body='.db_input($body) + .' WHERE `id`='.db_input($entry->getId()); + if (!db_query($sql) || !db_affected_rows()) + return false; + } + // Email message id (required for all thread posts) if (!isset($vars['mid'])) $vars['mid'] = sprintf('<%s@%s>', Misc::randCode(24), @@ -858,7 +1053,7 @@ Class ThreadEntry { $entry->saveEmailInfo($vars); // Inline images (attached to the draft) - $entry->saveAttachments(Draft::getAttachmentIds($vars['body'])); + $entry->saveAttachments(Draft::getAttachmentIds($body)); return $entry; } @@ -895,6 +1090,11 @@ class Message extends ThreadEntry { $vars['type'] = 'M'; $vars['body'] = $vars['message']; + if (!$vars['poster'] + && $vars['userId'] + && ($user = User::lookup($vars['userId']))) + $vars['poster'] = (string) $user->getName(); + return ThreadEntry::add($vars); } @@ -961,6 +1161,11 @@ class Response extends ThreadEntry { if(!$vars['pid'] && $vars['msgId']) $vars['pid'] = $vars['msgId']; + if (!$vars['poster'] + && $vars['staffId'] + && ($staff = Staff::lookup($vars['staffId']))) + $vars['poster'] = (string) $staff->getName(); + return ThreadEntry::add($vars); } @@ -1017,4 +1222,80 @@ class Note extends ThreadEntry { )?$n:null; } } + +class ThreadBody /* extends SplString */ { + + static $types = array('text', 'html'); + + var $body; + var $type; + var $stripped_images = array(); + + function __construct($body, $type='text') { + $type = strtolower($type); + if (!in_array($type, static::$types)) + throw new Exception($type.': Unsupported ThreadBody type'); + $this->body = (string) $body; + $this->type = $type; + } + + function convertTo($type) { + if ($type === $this->type) + return $this; + + $conv = $this->type . ':' . strtolower($type); + switch ($conv) { + case 'text:html': + return new ThreadBody(sprintf('<pre>%s</pre>', + Format::htmlchars($this->body)), $type); + case 'html:text': + return new ThreadBody(Format::html2text((string) $this), $type); + } + } + + function stripQuotedReply($tag) { + + //Strip quoted reply...on emailed messages + if (!$tag || strpos($this->body, $tag) === false) + return; + + // Capture a list of inline images + $images_before = $images_after = array(); + preg_match_all('/src="cid:([\w_-]+)(?:@|")/', $this->body, $images_before, + PREG_PATTERN_ORDER); + + // Strip the quoted part of the body + if ((list($msg) = explode($tag, $this->body, 2)) && trim($msg)) { + $this->body = $msg; + + // Capture a list of dropped inline images + if ($images_before) { + preg_match_all('/src="cid:([\w_-]+)(?:@|")/', $this->body, + $images_after, PREG_PATTERN_ORDER); + $this->stripped_images = array_diff($images_before[1], + $images_after[1]); + } + } + } + + function getStrippedImages() { + return $this->stripped_images; + } + + function __toString() { + return $this->body; + } +} + +class TextThreadBody extends ThreadBody { + function __construct($body) { + parent::__construct(Format::stripEmptyLines($body), 'text'); + } +} +class HtmlThreadBody extends ThreadBody { + function __construct($body) { + $body = trim($body, " <>br/\t\n\r") ? Format::safe_html($body) : ''; + parent::__construct($body, 'html'); + } +} ?> diff --git a/include/class.ticket.php b/include/class.ticket.php index 72ed7d8529fb0b9b1daa05ffd5e65868f755f66e..bd05210c3611429f2170cf4636edabda418bd7c3 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -31,6 +31,8 @@ include_once(INCLUDE_DIR.'class.sla.php'); include_once(INCLUDE_DIR.'class.canned.php'); require_once(INCLUDE_DIR.'class.dynamic_forms.php'); require_once(INCLUDE_DIR.'class.user.php'); +require_once(INCLUDE_DIR.'class.collaborator.php'); + class Ticket { @@ -62,15 +64,16 @@ class Ticket { return false; $sql='SELECT ticket.*, lock_id, dept_name ' - .' ,IF(sla.id IS NULL, NULL, DATE_ADD(ticket.created, INTERVAL sla.grace_period HOUR)) as sla_duedate ' - .' ,count(attach.attach_id) as attachments ' + .' ,IF(sla.id IS NULL, NULL, ' + .'DATE_ADD(ticket.created, INTERVAL sla.grace_period HOUR)) as sla_duedate ' + .' ,count(distinct attach.attach_id) as attachments' .' FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) ' - .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (' - .'ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' - .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (' - .'ticket.ticket_id=attach.ticket_id) ' + .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock + ON ( ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' + .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach + ON ( ticket.ticket_id=attach.ticket_id) ' .' WHERE ticket.ticket_id='.db_input($id) .' GROUP BY ticket.ticket_id'; @@ -82,7 +85,7 @@ class Ticket { $this->ht = db_fetch_array($res); $this->id = $this->ht['ticket_id']; - $this->number = $this->ht['ticketID']; + $this->number = $this->ht['number']; $this->_answers = array(); $this->loadDynamicData(); @@ -97,6 +100,7 @@ class Ticket { $this->stats = null; $this->topic = null; $this->thread = null; + $this->collaborators = null; return true; } @@ -167,17 +171,28 @@ class Ticket { return false; } - function checkClientAccess($client) { - global $cfg; + function checkUserAccess($user) { - if(!is_object($client) && !($client=Client::lookup($client))) + if (!$user || !($user instanceof EndUser)) return false; - if(!strcasecmp($client->getEmail(), $this->getEmail())) + //Ticket Owner + if ($user->getId() == $this->getUserId()) + return true; + + //Collaborator? + // 1) If the user was authorized via this ticket. + if ($user->getTicketId() == $this->getId() + && !strcasecmp($user->getRole(), 'collaborator')) return true; - return ($cfg && $cfg->showRelatedTickets() - && $client->getTicketId()==$this->getExtId()); + // 2) Query the database to check for expanded access... + if (Collaborator::lookup(array( + 'userId' => $user->getId(), + 'ticketId' => $this->getId()))) + return true; + + return false; } //Getters @@ -185,10 +200,6 @@ class Ticket { return $this->id; } - function getExtId() { - return $this->getNumber(); - } - function getNumber() { return $this->number; } @@ -198,9 +209,12 @@ class Ticket { } function getOwner() { - if (!isset($this->user)) - $this->user = User::lookup($this->getOwnerId()); - return $this->user; + + if (!isset($this->owner) + && ($u=User::lookup($this->getOwnerId()))) + $this->owner = new TicketOwner(new EndUser($u), $this); + + return $this->owner; } function getEmail(){ @@ -376,19 +390,25 @@ class Ticket { } function getDept() { + global $cfg; - if(!$this->dept && $this->getDeptId()) - $this->dept= Dept::lookup($this->getDeptId()); + if(!$this->dept) + if(!($this->dept = Dept::lookup($this->getDeptId()))) + $this->dept = $cfg->getDefaultDept(); return $this->dept; } - function getClient() { + function getUserId() { + return $this->getOwnerId(); + } + + function getUser() { - if(!$this->client) - $this->client = Client::lookup($this->getExtId(), $this->getEmail()); + if(!isset($this->user) && $this->getOwner()) + $this->user = new EndUser($this->getOwner()); - return $this->client; + return $this->user; } function getStaffId() { @@ -563,7 +583,112 @@ class Ticket { return $this->getThread()->getEntries($type, $order); } + //Collaborators + function getNumCollaborators() { + return count($this->getCollaborators()); + } + + function getNumActiveCollaborators() { + + if (!isset($this->ht['active_collaborators'])) + $this->ht['active_collaborators'] = count($this->getActiveCollaborators()); + + return $this->ht['active_collaborators']; + } + + function getActiveCollaborators() { + return $this->getCollaborators(array('isactive'=>1)); + } + + + function getCollaborators($criteria=array()) { + + if ($criteria) + return Collaborator::forTicket($this->getId(), $criteria); + + if (!isset($this->collaborators)) + $this->collaborators = Collaborator::forTicket($this->getId()); + + return $this->collaborators; + } + + //UserList of recipients (owner + collaborators) + function getRecipients() { + + if (!isset($this->recipients)) { + $list = new UserList(); + $list->add($this->getOwner()); + if ($collabs = $this->getActiveCollaborators()) { + foreach ($collabs as $c) + $list->add($c); + } + $this->recipients = $list; + } + + return $this->recipients; + } + + + function addCollaborator($user, $vars, &$errors) { + + if (!$user || $user->getId()==$this->getOwnerId()) + return null; + + $vars = array_merge(array( + 'ticketId' => $this->getId(), + 'userId' => $user->getId()), $vars); + if (!($c=Collaborator::add($vars, $errors))) + return null; + + $this->collaborators = null; + $this->recipients = null; + + return $c; + } + + //XXX: Ugly for now + function updateCollaborators($vars, &$errors) { + global $thisstaff; + + if (!$thisstaff) return; + + //Deletes + if($vars['del'] && ($ids=array_filter($vars['del']))) { + $collabs = array(); + foreach ($ids as $k => $cid) { + if (($c=Collaborator::lookup($cid)) + && $c->getTicketId() == $this->getId() + && $c->remove()) + $collabs[] = $c; + } + + $this->logNote('Collaborators Removed', + implode("<br>", $collabs), $thisstaff, false); + } + + //statuses + $cids = null; + if($vars['cid'] && ($cids=array_filter($vars['cid']))) { + $sql='UPDATE '.TICKET_COLLABORATOR_TABLE + .' SET updated=NOW(), isactive=1 ' + .' WHERE ticket_id='.db_input($this->getId()) + .' AND id IN('.implode(',', db_input($cids)).')'; + db_query($sql); + } + + $sql='UPDATE '.TICKET_COLLABORATOR_TABLE + .' SET updated=NOW(), isactive=0 ' + .' WHERE ticket_id='.db_input($this->getId()); + if($cids) + $sql.=' AND id NOT IN('.implode(',', db_input($cids)).')'; + + db_query($sql); + unset($this->ht['active_collaborators']); + $this->collaborators = null; + + return true; + } /* -------------------- Setters --------------------- */ function setLastMsgId($msgid) { @@ -755,43 +880,38 @@ class Ticket { /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/ $this->reload(); //get the new goodies. - $dept= $this->getDept(); - - if(!$dept || !($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); - - if(!$tpl) return false; //bail out...missing stuff. - - if(!$dept || !($email=$dept->getAutoRespEmail())) - $email =$cfg->getDefaultEmail(); + if(!$cfg + || !($dept=$this->getDept()) + || !($tpl = $dept->getTemplate()) + || !($email=$dept->getAutoRespEmail())) { + return false; //bail out...missing stuff. + } $options = array( 'inreplyto'=>$message->getEmailMessageId(), - 'references'=>$message->getEmailReferences()); + 'references'=>$message->getEmailReferences(), + 'thread'=>$message); //Send auto response - if enabled. - if($autorespond && $email && $cfg->autoRespONNewTicket() + if($autorespond + && $cfg->autoRespONNewTicket() && $dept->autoRespONNewTicket() && ($msg=$tpl->getAutoRespMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), array('message' => $message, + 'recipient' => $this->getOwner(), 'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'') ); - if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) - $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body']; - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], null, $options); } - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); - //Send alert to out sleepy & idle staff. - if($alertstaff && $email + if ($alertstaff && $cfg->alertONNewTicket() + && ($email=$cfg->getAlertEmail()) && ($msg=$tpl->getNewTicketAlertMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), array('message' => $message)); @@ -803,9 +923,8 @@ class Ticket { //Alert admin?? if($cfg->alertAdminONNewTicket()) { - $alert = str_replace('%{recipient}', 'Admin', $msg['body']); - $email->sendAlert($cfg->getAdminEmail(), $msg['subj'], - $alert, null, $options); + $alert = $this->replaceVars($msg, array('recipient' => 'Admin')); + $email->sendAlert($cfg->getAdminEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[]=$cfg->getAdminEmail(); } @@ -820,13 +939,10 @@ class Ticket { foreach( $recipients as $k=>$staff) { if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; - $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, - null, $options); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[] = $staff->getEmail(); } - - } return true; @@ -839,18 +955,14 @@ class Ticket { $msg=sprintf('Max open tickets (%d) reached for %s ', $cfg->getMaxOpenTickets(), $this->getEmail()); $ost->logWarning('Max. Open Tickets Limit ('.$this->getEmail().')', $msg); - if(!$sendNotice || !$cfg->sendOverLimitNotice()) return true; + if(!$sendNotice || !$cfg->sendOverLimitNotice()) + return true; //Send notice to user. - $dept = $this->getDept(); - - if(!$dept || !($tpl=$dept->getTemplate())) - $tpl=$cfg->getDefaultTemplate(); - - if(!$dept || !($email=$dept->getAutoRespEmail())) - $email=$cfg->getDefaultEmail(); - - if($tpl && ($msg=$tpl->getOverlimitMsgTemplate()) && $email) { + if(($dept = $this->getDept()) + && ($tpl=$dept->getTemplate()) + && ($msg=$tpl->getOverlimitMsgTemplate()) + && ($email=$dept->getAutoRespEmail())) { $msg = $this->replaceVars($msg->asArray(), array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')); @@ -858,11 +970,11 @@ class Ticket { $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body']); } - $client= $this->getClient(); + $user = $this->getOwner(); //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think. $alert='Max. open tickets reached for '.$this->getEmail()."\n" - .'Open ticket: '.$client->getNumOpenTickets()."\n" + .'Open ticket: '.$user->getNumOpenTickets()."\n" .'Max Allowed: '.$cfg->getMaxOpenTickets()."\n\nNotice sent to the user."; $ost->alertAdmin('Overlimit Notice', $alert); @@ -871,10 +983,61 @@ class Ticket { } function onResponse() { - db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1,lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId())); + db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1, lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId())); + $this->reload(); + } + + /* + * Notify collaborators on response or new message + * + */ + + function notifyCollaborators($entry, $vars = array()) { + global $cfg; + + if (!$entry instanceof ThreadEntry + || !($recipients=$this->getRecipients()) + || !($dept=$this->getDept()) + || !($tpl=$dept->getTemplate()) + || !($msg=$tpl->getActivityNoticeMsgTemplate()) + || !($email=$dept->getEmail())) + return; + + //Who posted the entry? + $uid = 0; + if ($entry instanceof Message) { + $poster = $entry->getUser(); + // Skip the person who sent in the message + $uid = $entry->getUserId(); + } else { + $poster = $entry->getStaff(); + // Skip the ticket owner + $uid = $this->getUserId(); + } + + $vars = array_merge($vars, array( + 'message' => (string) $entry, + 'poster' => $poster? $poster : 'A collaborator', + ) + ); + + $msg = $this->replaceVars($msg->asArray(), $vars); + + $attachments = $cfg->emailAttachments()?$entry->getAttachments():array(); + $options = array('inreplyto' => $entry->getEmailMessageId(), + 'thread' => $entry); + foreach ($recipients as $recipient) { + if ($uid == $recipient->getId()) continue; + $options['references'] = $entry->getEmailReferencesForUser($recipient); + $notice = $this->replaceVars($msg, array('recipient' => $recipient)); + $email->send($recipient->getEmail(), $notice['subj'], $notice['body'], $attachments, + $options); + } + + return; } - function onMessage($autorespond=true, $message=null) { + function onMessage($message, $autorespond=true) { global $cfg; db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastmessage=NOW() WHERE ticket_id='.db_input($this->getId())); @@ -901,39 +1064,37 @@ class Ticket { if($this->isClosed()) $this->reopen(); /********** double check auto-response ************/ - if($autorespond && (Email::getIdByEmail($this->getEmail()))) + if (!($user = $message->getUser())) $autorespond=false; - elseif($autorespond && ($dept=$this->getDept())) + elseif ($autorespond && (Email::getIdByEmail($user->getEmail()))) + $autorespond=false; + elseif ($autorespond && ($dept=$this->getDept())) $autorespond=$dept->autoRespONNewMessage(); - if(!$autorespond || !$cfg->autoRespONNewMessage()) return; //no autoresp or alerts. + if(!$autorespond + || !$cfg->autoRespONNewMessage() + || !$message) return; //no autoresp or alerts. $this->reload(); - - - if(!$dept || !($tpl = $dept->getTemplate())) - $tpl = $cfg->getDefaultTemplate(); - - if(!$dept || !($email = $dept->getAutoRespEmail())) - $email = $cfg->getDefaultEmail(); + $dept = $this->getDept(); + $email = $dept->getAutoRespEmail(); //If enabled...send confirmation to user. ( New Message AutoResponse) - if($email && $tpl && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) { + if($email + && ($tpl=$dept->getTemplate()) + && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), - array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')); - - //Reply separator tag. - if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) - $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body']; + array( + 'recipient' => $user, + 'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')); - if (!$message) - $message = $this->getLastMessage(); $options = array( 'inreplyto'=>$message->getEmailMessageId(), - 'references'=>$message->getEmailReferences()); - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], + 'references' => $message->getEmailReferencesForUser($user), + 'thread'=>$message); + $email->sendAutoReply($user->getEmail(), $msg['subj'], $msg['body'], null, $options); } } @@ -959,14 +1120,10 @@ class Ticket { if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts! $dept = $this->getDept(); - - //Get template. - if(!$dept || !($tpl = $dept->getTemplate())) - $tpl = $cfg->getDefaultTemplate(); - - //Email to use! - if(!($email=$cfg->getAlertEmail())) - $email = $cfg->getDefaultEmail(); + if(!$dept + || !($tpl = $dept->getTemplate()) + || !($email = $cfg->getAlertEmail())) + return true; //recipients $recipients=array(); @@ -981,7 +1138,7 @@ class Ticket { } //Get the message template - if($email && $recipients && $tpl && ($msg=$tpl->getAssignedAlertMsgTemplate())) { + if($recipients && ($msg=$tpl->getAssignedAlertMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), array('comments' => $comments, @@ -993,12 +1150,12 @@ class Ticket { $sentlist=array(); $options = array( 'inreplyto'=>$note->getEmailMessageId(), - 'references'=>$note->getEmailReferences()); + 'references'=>$note->getEmailReferences(), + 'thread'=>$note); foreach( $recipients as $k=>$staff) { if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; - $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, - null, $options); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[] = $staff->getEmail(); } } @@ -1013,20 +1170,15 @@ class Ticket { $whine = false; //check if we need to send alerts. - if(!$whine || !$cfg->alertONOverdueTicket()) + if(!$whine + || !$cfg->alertONOverdueTicket() + || !($dept = $this->getDept())) return true; - $dept = $this->getDept(); - //Get department-defined or default template. - if(!$dept || !($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); - - //Email to use! - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); - //Get the message template - if($tpl && ($msg=$tpl->getOverdueAlertMsgTemplate()) && $email) { + if(($tpl = $dept->getTemplate()) + && ($msg=$tpl->getOverdueAlertMsgTemplate()) + && ($email=$cfg->getAlertEmail())) { $msg = $this->replaceVars($msg->asArray(), array('comments' => $comments)); @@ -1051,9 +1203,8 @@ class Ticket { $sentlist=array(); foreach( $recipients as $k=>$staff) { if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; - $alert = str_replace("%{recipient}", $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, - null); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null); $sentlist[] = $staff->getEmail(); } @@ -1221,19 +1372,12 @@ class Ticket { $this->logEvent('transferred'); //Send out alerts if enabled AND requested - if(!$alert || !$cfg->alertONTransfer() || !($dept=$this->getDept())) return true; //no alerts!! - - - //Get template. - if(!($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); - - //Email to use! - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); + if(!$alert || !$cfg->alertONTransfer() || !($dept=$this->getDept())) + return true; //no alerts!! - //Get the message template - if($tpl && ($msg=$tpl->getTransferAlertMsgTemplate()) && $email) { + if(($email=$cfg->getAlertEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg=$tpl->getTransferAlertMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), array('comments' => $comments, 'staff' => $thisstaff)); @@ -1258,12 +1402,12 @@ class Ticket { $sentlist=array(); $options = array( 'inreplyto'=>$note->getEmailMessageId(), - 'references'=>$note->getEmailReferences()); + 'references'=>$note->getEmailReferences(), + 'thread'=>$note); foreach( $recipients as $k=>$staff) { if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; - $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, - null, $options); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[] = $staff->getEmail(); } } @@ -1366,11 +1510,20 @@ class Ticket { $this->ht['user_id'] = $user->getId(); $this->user = null; + $this->collaborators = null; + $this->recipients = null; - $this->logNote('Ticket ownership changed', - Format::htmlchars( sprintf('%s changed ticket ownership to %s', - $thisstaff->getName(), $user->getName())) - ); + //Log an internal note + $note = sprintf('%s changed ticket ownership to %s', + $thisstaff->getName(), $user->getName()); + + //Remove the new owner from list of collaborators + $c = Collaborator::lookup(array('userId' => $user->getId(), + 'ticketId' => $this->getId())); + if ($c && $c->remove()) + $note.= ' (removed as collaborator)'; + + $this->logNote('Ticket ownership changed', $note); return true; } @@ -1379,14 +1532,7 @@ class Ticket { function postMessage($vars, $origin='', $alerts=true) { global $cfg; - //Strip quoted reply...on emailed replies - if(!strcasecmp($origin, 'Email') - && $cfg->stripQuotedReply() - && ($tag=$cfg->getReplySeparator()) - && strpos($vars['message'], $tag)) - if((list($msg) = explode($tag, $vars['message'], 2)) && trim($msg)) - $vars['message'] = $msg; - + $vars['origin'] = $origin; if(isset($vars['ip'])) $vars['ip_address'] = $vars['ip']; elseif(!$vars['ip_address'] && $_SERVER['REMOTE_ADDR']) @@ -1398,6 +1544,36 @@ class Ticket { $this->setLastMsgId($message->getId()); + //Add email recipients as collaborators... + if ($vars['recipients'] + && (strtolower($origin) != 'email' || ($cfg && $cfg->addCollabsViaEmail())) + //Only add if we have a matched local address + && $vars['to-email-id']) { + //New collaborators added by other collaborators are disable -- + // requires staff approval. + $info = array( + 'isactive' => ($message->getUserId() == $this->getUserId())? 1: 0); + $collabs = array(); + foreach ($vars['recipients'] as $recipient) { + // Skip virtual delivered-to addresses + if (strcasecmp($recipient['source'], 'delivered-to') === 0) + continue; + + if (($user=User::fromVars($recipient))) + if ($c=$this->addCollaborator($user, $info, $errors)) + $collabs[] = sprintf('%s%s', + (string) $c, + $recipient['source'] ? " via {$recipient['source']}" : '' + ); + } + //TODO: Can collaborators add others? + if ($collabs) { + //TODO: Change EndUser to name of user. + $this->logNote('Collaborators added by enduser', + implode("<br>", $collabs), 'EndUser', false); + } + } + if(!$alerts) return $message; //Our work is done... // Do not auto-respond to bounces and other auto-replies @@ -1405,20 +1581,29 @@ class Ticket { if ($autorespond && $message->isAutoReply()) $autorespond = false; - $this->onMessage($autorespond, $message); //must be called b4 sending alerts to staff. + $this->onMessage($message, $autorespond); //must be called b4 sending alerts to staff. - $dept = $this->getDept(); + if ($autorespond && $cfg && $cfg->notifyCollabsONNewMessage()) + $this->notifyCollaborators($message, array('signature' => '')); - if(!$dept || !($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); + $dept = $this->getDept(); - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); + $variables = array( + 'message' => $message, + 'poster' => ($vars['poster'] ? $vars['poster'] : $this->getName()) + ); + $options = array( + 'inreplyto' => $message->getEmailMessageId(), + 'references' => $message->getEmailReferences(), + 'thread'=>$message); //If enabled...send alert to staff (New Message Alert) - if($cfg->alertONNewMessage() && $tpl && $email && ($msg=$tpl->getNewMessageAlertMsgTemplate())) { + if($cfg->alertONNewMessage() + && ($email = $cfg->getAlertEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg = $tpl->getNewMessageAlertMsgTemplate())) { - $msg = $this->replaceVars($msg->asArray(), array('message' => $message)); + $msg = $this->replaceVars($msg->asArray(), $variables); //Build list of recipients and fire the alerts. $recipients=array(); @@ -1436,14 +1621,10 @@ class Ticket { $recipients[]=$manager; $sentlist=array(); //I know it sucks...but..it works. - $options = array( - 'inreplyto'=>$message->getEmailMessageId(), - 'references'=>$message->getEmailReferences()); foreach( $recipients as $k=>$staff) { if(!$staff || !$staff->getEmail() || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue; - $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, - null, $options); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[] = $staff->getEmail(); } } @@ -1476,13 +1657,9 @@ class Ticket { $dept = $this->getDept(); - if(!($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); - - if(!$dept || !($email=$dept->getEmail())) - $email = $cfg->getDefaultEmail(); - - if($tpl && ($msg=$tpl->getAutoReplyMsgTemplate()) && $email) { + if(($email=$dept->getEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg=$tpl->getAutoReplyMsgTemplate())) { if($dept && $dept->isPublic()) $signature=$dept->getSignature(); @@ -1492,13 +1669,11 @@ class Ticket { $msg = $this->replaceVars($msg->asArray(), array('response' => $response, 'signature' => $signature)); - if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) - $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body']; - $attachments =($cfg->emailAttachments() && $files)?$response->getAttachments():array(); $options = array( 'inreplyto'=>$response->getEmailMessageId(), - 'references'=>$response->getEmailReferences()); + 'references'=>$response->getEmailReferences(), + 'thread'=>$response); $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], $attachments, $options); } @@ -1512,7 +1687,7 @@ class Ticket { if(!$vars['poster'] && $thisstaff) - $vars['poster'] = $thisstaff->getName(); + $vars['poster'] = $thisstaff; if(!$vars['staffId'] && $thisstaff) $vars['staffId'] = $thisstaff->getId(); @@ -1529,45 +1704,45 @@ class Ticket { $this->setStaffId($thisstaff->getId()); //direct assignment; $this->onResponse(); //do house cleaning.. - $this->reload(); /* email the user?? - if disabled - the bail out */ if(!$alert) return $response; $dept = $this->getDept(); - if(!($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); + if($thisstaff && $vars['signature']=='mine') + $signature=$thisstaff->getSignature(); + elseif($vars['signature']=='dept' && $dept && $dept->isPublic()) + $signature=$dept->getSignature(); + else + $signature=''; - if(!$dept || !($email=$dept->getEmail())) - $email = $cfg->getDefaultEmail(); - - if($tpl && ($msg=$tpl->getReplyMsgTemplate()) && $email) { - - if($thisstaff && $vars['signature']=='mine') - $signature=$thisstaff->getSignature(); - elseif($vars['signature']=='dept' && $dept && $dept->isPublic()) - $signature=$dept->getSignature(); - else - $signature=''; + $variables = array( + 'response' => $response, + 'signature' => $signature, + 'staff' => $thisstaff, + 'poster' => $thisstaff); + $options = array( + 'inreplyto' => $response->getEmailMessageId(), + 'references' => $response->getEmailReferences(), + 'thread'=>$response); - //Set attachments if emailing. - $attachments = $cfg->emailAttachments()?$response->getAttachments():array(); + if(($email=$dept->getEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg=$tpl->getReplyMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), - array('response' => $response, 'signature' => $signature, 'staff' => $thisstaff)); - - if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator())) - $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body']; + $variables + array('recipient' => $this->getOwner())); - $options = array( - 'inreplyto' => $response->getEmailMessageId(), - 'references' => $response->getEmailReferences()); - //TODO: setup 5 param (options... e.g mid trackable on replies) + $attachments = $cfg->emailAttachments()?$response->getAttachments():array(); $email->send($this->getEmail(), $msg['subj'], $msg['body'], $attachments, $options); } + if($vars['emailcollab']) + $this->notifyCollaborators($response, + array('signature' => $signature)); + return $response; } @@ -1608,11 +1783,19 @@ class Ticket { function logNote($title, $note, $poster='SYSTEM', $alert=true) { $errors = array(); + //Unless specified otherwise, assume HTML + if ($note && is_string($note)) + $note = new HtmlThreadBody($note); + return $this->postNote( - array('title' => $title, 'note' => $note), + array( + 'title' => $title, + 'note' => $note, + ), $errors, $poster, - $alert); + $alert + ); } function postNote($vars, &$errors, $poster, $alert=true) { @@ -1645,14 +1828,9 @@ class Ticket { if(!$alert || !$cfg->alertONNewNote() || !($dept=$this->getDept())) return $note; - if(!($tpl = $dept->getTemplate())) - $tpl= $cfg->getDefaultTemplate(); - - if(!($email=$cfg->getAlertEmail())) - $email =$cfg->getDefaultEmail(); - - - if($tpl && ($msg=$tpl->getNoteAlertMsgTemplate()) && $email) { + if(($email=$cfg->getAlertEmail()) + && ($tpl = $dept->getTemplate()) + && ($msg=$tpl->getNoteAlertMsgTemplate())) { $msg = $this->replaceVars($msg->asArray(), array('note' => $note)); @@ -1674,7 +1852,8 @@ class Ticket { $options = array( 'inreplyto'=>$note->getEmailMessageId(), - 'references'=>$note->getEmailReferences()); + 'references'=>$note->getEmailReferences(), + 'thread'=>$note); $sentlist=array(); foreach( $recipients as $k=>$staff) { if(!is_object($staff) @@ -1682,9 +1861,8 @@ class Ticket { || in_array($staff->getEmail(), $sentlist) //No duplicates. || $note->getStaffId() == $staff->getId()) //No need to alert the poster! continue; - $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']); - $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, null, - $options); + $alert = $this->replaceVars($msg, array('recipient' => $staff)); + $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options); $sentlist[] = $staff->getEmail(); } } @@ -1702,7 +1880,7 @@ class Ticket { $psize = 'Letter'; $pdf = new Ticket2PDF($this, $psize, $notes); - $name='Ticket-'.$this->getExtId().'.pdf'; + $name='Ticket-'.$this->getNumber().'.pdf'; $pdf->Output($name, 'I'); //Remember what the user selected - for autoselect on the next print. $_SESSION['PAPER_SIZE'] = $psize; @@ -1810,15 +1988,15 @@ class Ticket { /*============== Static functions. Use Ticket::function(params); =============nolint*/ - function getIdByExtId($extId, $email=null) { + function getIdByNumber($number, $email=null) { - if(!$extId || !is_numeric($extId)) + if(!$number) return 0; $sql ='SELECT ticket.ticket_id FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' - .' WHERE ticket.ticketID='.db_input($extId); + .' WHERE ticket.`number`='.db_input($number); if($email) $sql .= ' AND email.address = '.db_input($email); @@ -1839,20 +2017,20 @@ class Ticket { ?$ticket:null; } - function lookupByExtId($id, $email=null) { - return self::lookup(self:: getIdByExtId($id, $email)); + function lookupByNumber($number, $email=null) { + return self::lookup(self:: getIdByNumber($number, $email)); } - function genExtRandID() { - global $cfg; + function genRandTicketNumber($len = EXT_TICKET_ID_LEN) { - //We can allow collissions...extId and email must be unique ...so same id with diff emails is ok.. - // But for clarity...we are going to make sure it is unique. - $id=Misc::randNumber(EXT_TICKET_ID_LEN); - if(db_num_rows(db_query('SELECT ticket_id FROM '.TICKET_TABLE.' WHERE ticketID='.db_input($id)))) - return Ticket::genExtRandID(); + //We can allow collissions...number and email must be unique ...so + // same number with diff emails is ok.. But for clarity...we are going to make sure it is unique. + $number = Misc::randNumber($len); + if(db_num_rows(db_query('SELECT ticket_id FROM '.TICKET_TABLE.' + WHERE `number`='.db_input($number)))) + return Ticket::genRandTicketNumber($len); - return $id; + return $number; } function getIdByMessageId($mid, $email) { @@ -1934,11 +2112,8 @@ class Ticket { /* Quick client's tickets stats @email - valid email. */ - function getClientStats($email) { - - if(!$email || !Validator::is_email($email)) - return null; - if (!$user = User::lookup(array('emails__address'=>$email))) + function getUserStats($user) { + if(!$user || !($user instanceof EndUser)) return null; $sql='SELECT count(open.ticket_id) as open, count(closed.ticket_id) as closed ' @@ -1982,32 +2157,6 @@ class Ticket { }; }; - //Check for 403 - if ($vars['email'] && Validator::is_email($vars['email'])) { - - //Make sure the email address is not banned - if(TicketFilter::isBanned($vars['email'])) { - $errors['err']='Ticket denied. Error #403'; - $errors['errno'] = 403; - $ost->logWarning('Ticket denied', 'Banned email - '.$vars['email']); - return 0; - } - - //Make sure the open ticket limit hasn't been reached. (LOOP CONTROL) - if($cfg->getMaxOpenTickets()>0 && strcasecmp($origin,'staff') - && ($client=Client::lookupByEmail($vars['email'])) - && ($openTickets=$client->getNumOpenTickets()) - && ($openTickets>=$cfg->getMaxOpenTickets()) ) { - - $errors['err']="You've reached the maximum open tickets allowed."; - $ost->logWarning('Ticket denied -'.$vars['email'], - sprintf('Max open tickets (%d) reached for %s ', - $cfg->getMaxOpenTickets(), $vars['email'])); - - return 0; - } - } - // Create and verify the dynamic form entry for the new ticket $form = TicketForm::getNewInstance(); // If submitting via email, ensure we have a subject and such @@ -2037,13 +2186,46 @@ class Ticket { $vars[$f->get('name')] = $f->toString($f->getClean()); } + + //Check for 403 + if ($vars['email'] + && Validator::is_email($vars['email'])) { + + //Make sure the email address is not banned + if (TicketFilter::isBanned($vars['email'])) { + $errors = array( + 'errno' => 403, + 'err' => 'This help desk is for use by authorized + users only'); + $ost->logWarning('Ticket denied', 'Banned email - '.$vars['email']); + return 0; + } + + //Make sure the open ticket limit hasn't been reached. (LOOP CONTROL) + if ($cfg->getMaxOpenTickets() > 0 + && strcasecmp($origin, 'staff') + && ($_user=TicketUser::lookupByEmail($vars['email'])) + && ($openTickets=$_user->getNumOpenTickets()) + && ($openTickets>=$cfg->getMaxOpenTickets()) ) { + + $errors = array('err' => "You've reached the maximum open tickets allowed."); + $ost->logWarning('Ticket denied -'.$vars['email'], + sprintf('Max open tickets (%d) reached for %s ', + $cfg->getMaxOpenTickets(), $vars['email'])); + + return 0; + } + } + //Init ticket filters... $ticket_filter = new TicketFilter($origin, $vars); // Make sure email contents should not be rejected if($ticket_filter && ($filter=$ticket_filter->shouldReject())) { - $errors['err']='Ticket denied. Error #403'; - $errors['errno'] = 403; + $errors = array( + 'errno' => 403, + 'err' => "This help desk is for use by authorized users + only"); $ost->logWarning('Ticket denied', sprintf('Ticket rejected ( %s) by filter "%s"', $vars['email'], $filter->getName())); @@ -2053,7 +2235,7 @@ class Ticket { $id=0; $fields=array(); - $fields['message'] = array('type'=>'text', 'required'=>1, 'error'=>'Message required'); + $fields['message'] = array('type'=>'*', 'required'=>1, 'error'=>'Message required'); switch (strtolower($origin)) { case 'web': $fields['topicId'] = array('type'=>'int', 'required'=>1, 'error'=>'Select help topic'); @@ -2096,7 +2278,7 @@ class Ticket { if (!$user) { $user_form = UserForm::getUserForm()->getForm($vars); if (!$user_form->isValid($field_filter('user')) - || !($user=User::fromForm($user_form->getClean()))) + || !($user=User::fromVars($user_form->getClean()))) $errors['user'] = 'Incomplete client information'; } } @@ -2155,16 +2337,19 @@ class Ticket { $ipaddress=$vars['ip']?$vars['ip']:$_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. - $extId=Ticket::genExtRandID(); + $number = Ticket::genRandTicketNumber(); $sql='INSERT INTO '.TICKET_TABLE.' SET created=NOW() ' .' ,lastmessage= NOW()' - .' ,user_id='.db_input($user->id) - .' ,ticketID='.db_input($extId) + .' ,user_id='.db_input($user->getId()) + .' ,`number`='.db_input($number) .' ,dept_id='.db_input($deptId) .' ,topic_id='.db_input($topicId) .' ,ip_address='.db_input($ipaddress) .' ,source='.db_input($source); + if (isset($vars['emailId']) && $vars['emailId']) + $sql.=', email_id='.db_input($vars['emailId']); + //Make sure the origin is staff - avoid firebug hack! if($vars['duedate'] && !strcasecmp($origin,'staff')) $sql.=' ,duedate='.db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))); @@ -2176,9 +2361,9 @@ class Ticket { /* -------------------- POST CREATE ------------------------ */ if(!$cfg->useRandomIds()) { - //Sequential ticketIDs support really..really suck arse. - $extId=$id; //To make things really easy we are going to use autoincrement ticket_id. - db_query('UPDATE '.TICKET_TABLE.' SET ticketID='.db_input($extId).' WHERE ticket_id='.$id.' LIMIT 1'); + //Sequential ticket number support really..really suck arse. + //To make things really easy we are going to use autoincrement ticket_id. + db_query('UPDATE '.TICKET_TABLE.' SET `number`='.db_input($id).' WHERE ticket_id='.$id.' LIMIT 1'); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } @@ -2192,6 +2377,7 @@ class Ticket { //post the message. unset($vars['cannedattachments']); //Ticket::open() might have it set as part of open & respond. $vars['title'] = $vars['subject']; //Use the initial subject as title of the post. + $vars['userId'] = $ticket->getUserId(); $message = $ticket->postMessage($vars , $origin, false); // Configure service-level-agreement for this ticket @@ -2237,8 +2423,8 @@ class Ticket { /************ check if the user JUST reached the max. open tickets limit **********/ if($cfg->getMaxOpenTickets()>0 - && ($client=$ticket->getClient()) - && ($client->getNumOpenTickets()==$cfg->getMaxOpenTickets())) { + && ($user=$ticket->getOwner()) + && ($user->getNumOpenTickets()==$cfg->getMaxOpenTickets())) { $ticket->onOpenLimit(($autorespond && strcasecmp($origin, 'staff'))); } @@ -2250,7 +2436,8 @@ class Ticket { return $ticket; } - function open($vars, &$errors) { + /* routine used by staff to open a new ticket */ + static function open($vars, &$errors) { global $thisstaff, $cfg; if(!$thisstaff || !$thisstaff->canCreateTickets()) return false; @@ -2299,19 +2486,15 @@ class Ticket { $ticket->reload(); - if(!$cfg->notifyONNewStaffTicket() || !isset($vars['alertuser'])) + if(!$cfg->notifyONNewStaffTicket() + || !isset($vars['alertuser']) + || !($dept=$ticket->getDept())) return $ticket; //No alerts. //Send Notice to user --- if requested AND enabled!! - - $dept=$ticket->getDept(); - if(!$dept || !($tpl=$dept->getTemplate())) - $tpl=$cfg->getDefaultTemplate(); - - if(!$dept || !($email=$dept->getEmail())) - $email =$cfg->getDefaultEmail(); - - if($tpl && ($msg=$tpl->getNewTicketNoticeMsgTemplate()) && $email) { + if(($tpl=$dept->getTemplate()) + && ($msg=$tpl->getNewTicketNoticeMsgTemplate()) + && ($email=$dept->getEmail())) { $message = $vars['message']; if($response) { @@ -2328,19 +2511,23 @@ class Ticket { $attachments =($cfg->emailAttachments() && $response)?$response->getAttachments():array(); - $msg = $ticket->replaceVars($msg->asArray(), array( - 'message' => $message, - 'signature' => $signature, - 'response' => ($response) ? $response->getBody() : '', - )); - - if($cfg->stripQuotedReply() && ($tag=trim($cfg->getReplySeparator()))) - $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body']; + $msg = $ticket->replaceVars($msg->asArray(), + array( + 'message' => $message, + 'signature' => $signature, + 'response' => ($response) ? $response->getBody() : '', + 'recipient' => $ticket->getOwner(), //End user + 'staff' => $thisstaff, + ) + ); $references = $ticket->getLastMessage()->getEmailMessageId(); if (isset($response)) $references = array($response->getEmailMessageId(), $references); - $options = array('references' => $references); + $options = array( + 'references' => $references, + 'thread' => $ticket->getLastMessage() + ); $email->send($ticket->getEmail(), $msg['subj'], $msg['body'], $attachments, $options); } diff --git a/include/class.user.php b/include/class.user.php index c6c6b7535f6c2dd3e30bfd2f9f28993ecff70542..0a945f68b57c1b37ed93c59fa40fbd7b5ba45969 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -76,33 +76,47 @@ class User extends UserModel { $this->default_email = UserEmail::lookup($ht['default_email_id']); } - static function fromForm($data=false) { + static function fromVars($vars) { // Try and lookup by email address - $user = User::lookup(array('emails__address'=>$data['email'])); + $user = User::lookup(array('emails__address'=>$vars['email'])); if (!$user) { $user = User::create(array( - 'name'=>$data['name'], + 'name'=>$vars['name'], 'created'=>new SqlFunction('NOW'), 'updated'=>new SqlFunction('NOW'), //XXX: Do plain create once the cause // of the detached emails is fixed. - 'default_email' => UserEmail::ensure($data['email']) + 'default_email' => UserEmail::ensure($vars['email']) )); $user->save(true); $user->emails->add($user->default_email); - // Attach initial custom fields - $uf = UserForm::getInstance(); - foreach ($uf->getFields() as $f) - if (isset($data[$f->get('name')])) - $uf->setAnswer($f->get('name'), $data[$f->get('name')]); - $uf->setClientId($user->id); - $uf->save(); + $user->addDynamicData($vars); } return $user; } + static function fromForm($form) { + + if(!$form) return null; + + //Validate the form + $valid = true; + if (!$form->isValid()) + $valid = false; + + //Make sure the email is not in-use + if (($field=$form->getField('email')) + && $field->getClean() + && User::lookup(array('emails__address'=>$field->getClean()))) { + $field->addError('Email is assigned to another user'); + $valid = false; + } + + return $valid ? self::fromVars($form->getClean()) : null; + } + function getEmail() { return $this->default_email->address; } @@ -125,6 +139,10 @@ class User extends UserModel { return $this->updated; } + function getCreateDate() { + return $this->created; + } + function to_json() { $info = array( @@ -150,6 +168,18 @@ class User extends UserModel { return $a; } + function addDynamicData($data) { + + $uf = UserForm::getInstance(); + $uf->setClientId($this->id); + foreach ($uf->getFields() as $f) + if (isset($data[$f->get('name')])) + $uf->setAnswer($f->get('name'), $data[$f->get('name')]); + $uf->save(); + + return $uf; + } + function getDynamicData() { if (!isset($this->_entries)) { $this->_entries = DynamicFormEntry::forClient($this->id)->all(); @@ -354,6 +384,10 @@ class PersonsName { return mb_convert_case($initials, MB_CASE_UPPER); } + function getName() { + return $this; + } + function asVar() { return $this->__toString(); } @@ -434,3 +468,56 @@ class UserEmail extends UserEmailModel { return $email; } } + + +/* + * Generic user list. + */ +class UserList implements IteratorAggregate, ArrayAccess { + private $users; + + function __construct($list = array()) { + $this->users = $list; + } + + function add($user) { + $this->offsetSet(null, $user); + } + + function offsetSet($offset, $value) { + + if (is_null($offset)) + $this->users[] = $value; + else + $this->users[$offset] = $value; + } + + function offsetExists($offset) { + return isset($this->users[$offset]); + } + + function offsetUnset($offset) { + unset($this->users[$offset]); + } + + function offsetGet($offset) { + return isset($this->users[$offset]) ? $this->users[$offset] : null; + } + + function getIterator() { + return new ArrayIterator($this->users); + } + + function __toString() { + + $list = array(); + foreach($this->users as $user) { + if (is_object($user)) + $list [] = $user->getName(); + } + + return $list ? implode(', ', $list) : ''; + } +} + +?> diff --git a/include/class.usersession.php b/include/class.usersession.php index 85aa876eb36e957d116c4b7bea3e10f345cd3096..31c4ce058d50939e10dabda80fe736ab3095c83f 100644 --- a/include/class.usersession.php +++ b/include/class.usersession.php @@ -111,13 +111,16 @@ class UserSession { } -class ClientSession extends Client { +class ClientSession extends EndUser { var $session; + var $token; - function ClientSession($email, $id){ - parent::Client($id, $email); - $this->session= new UserSession(strtolower($email)); + function __construct($user) { + parent::__construct($user); + $this->token = &$_SESSION[':token']['client']; + // XXX: Change the key to user-id + $this->session= new UserSession($user->getUserName()); } function isValid(){ @@ -126,15 +129,16 @@ class ClientSession extends Client { if(!$this->getId() || $this->session->getSessionId()!=session_id()) return false; - return $this->session->isvalidSession($_SESSION['_client']['token'],$cfg->getClientTimeout(),false)?true:false; + return $this->session->isvalidSession($this->token,$cfg->getClientTimeout(),false)?true:false; } function refreshSession($force=false){ - $time = $this->session->getLastUpdate($_SESSION['_client']['token']); + $time = $this->session->getLastUpdate($this->token); // Deadband session token updates to once / 30-seconds if (!$force && time() - $time < 30) return; - $_SESSION['_client']['token']=$this->getSessionToken(); + + $this->token = $this->getSessionToken(); //TODO: separate expire time from hash?? } @@ -155,28 +159,30 @@ class ClientSession extends Client { class StaffSession extends Staff { var $session; + var $token; - function StaffSession($var){ - parent::Staff($var); + function __construct($var) { + parent::__construct($var); + $this->token = &$_SESSION[':token']['staff']; $this->session= new UserSession($this->getId()); } function isValid(){ - global $_SESSION,$cfg; + global $_SESSION, $cfg; if(!$this->getId() || $this->session->getSessionId()!=session_id()) return false; - return $this->session->isvalidSession($_SESSION['_staff']['token'],$cfg->getStaffTimeout(),$cfg->enableStaffIPBinding())?true:false; + return $this->session->isvalidSession($this->token,$cfg->getStaffTimeout(),$cfg->enableStaffIPBinding())?true:false; } function refreshSession($force=false){ - $time = $this->session->getLastUpdate($_SESSION['_staff']['token']); + $time = $this->session->getLastUpdate($this->token); // Deadband session token updates to once / 30-seconds if (!$force && time() - $time < 30) return; - $_SESSION['_staff']['token']=$this->getSessionToken(); + $this->token=$this->getSessionToken(); } function getSession() { diff --git a/include/class.validator.php b/include/class.validator.php index f783adb40fbe317bb31cee96e759a639f1b64711..c5cd2283eb362a58325e6291a3d2c5f245816116 100644 --- a/include/class.validator.php +++ b/include/class.validator.php @@ -61,6 +61,10 @@ class Validator { $this->errors[$k]=$field['error']; continue; } + + //We don't care about the type. + if ($field['type'] == '*') continue; + //Do the actual validation based on the type. switch(strtolower($field['type'])): case 'integer': @@ -73,7 +77,7 @@ class Validator { case 'double': if(!is_numeric($this->input[$k])) $this->errors[$k]=$field['error']; - break; + break; case 'text': case 'string': if(!is_string($this->input[$k])) @@ -84,9 +88,9 @@ class Validator { $this->errors[$k]=$field['error']; break; case 'radio': - if(!isset($this->input[$k])) - $this->errors[$k]=$field['error']; - break; + if(!isset($this->input[$k])) + $this->errors[$k]=$field['error']; + break; case 'date': //TODO...make sure it is really in GNU date format.. if(strtotime($this->input[$k])===false) $this->errors[$k]=$field['error']; diff --git a/include/class.variable.php b/include/class.variable.php index 36d49e6a39accdcdee68aaa06cd8059779474e00..233c9fc18696886460991b35bf3dba18701762cc 100644 --- a/include/class.variable.php +++ b/include/class.variable.php @@ -63,11 +63,15 @@ class VariableReplacer { if(!$obj) return ""; - if(!$var && is_callable(array($obj, 'asVar'))) - return call_user_func(array($obj, 'asVar')); + if (!$var) { + if (method_exists($obj, 'asVar')) + return call_user_func(array($obj, 'asVar')); + elseif (method_exists($obj, '__toString')) + return (string) $obj; + } list($v, $part) = explode('.', $var, 2); - if($v && is_callable(array($obj, 'get'.ucfirst($v)))) { + if ($v && is_callable(array($obj, 'get'.ucfirst($v)))) { $rv = call_user_func(array($obj, 'get'.ucfirst($v))); if(!$rv || !is_object($rv)) return $rv; @@ -75,7 +79,7 @@ class VariableReplacer { return $this->getVar($rv, $part); } - if(!$var || !is_callable(array($obj, 'getVar'))) + if (!$var || !method_exists($obj, 'getVar')) return ""; $parts = explode('.', $var); diff --git a/include/class.yaml.php b/include/class.yaml.php index fc340d70abc0153b3e3f8071b8981c3da17f5cbc..63ceb37543d3fe4044172e1a7775c84e6734feb8 100644 --- a/include/class.yaml.php +++ b/include/class.yaml.php @@ -37,6 +37,6 @@ class YamlDataParser { } class YamlParserError extends Error { - var $title = 'Error parsing YAML document'; + static $title = 'Error parsing YAML document'; } ?> diff --git a/include/client/edit.inc.php b/include/client/edit.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..21f2ca726b1863bfe1b5f5eaf0f946606b0830b4 --- /dev/null +++ b/include/client/edit.inc.php @@ -0,0 +1,30 @@ +<?php + +if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkUserAccess($thisclient)) die('Access Denied!'); + +?> + +<h1> + Editing Ticket #<?php echo $ticket->getNumber(); ?> +</h1> + +<form action="tickets.php" method="post"> + <?php echo csrf_token(); ?> + <input type="hidden" name="a" value="edit"/> + <input type="hidden" name="id" value="<?php echo $_REQUEST['id']; ?>"/> +<table width="800"> + <tbody id="dynamic-form"> + <?php if ($forms) + foreach ($forms as $form) { + $form->render(false); + } ?> + </tbody> +</table> +<hr> +<p style="text-align: center;"> + <input type="submit" value="Update"/> + <input type="reset" value="Reset"/> + <input type="button" value="Cancel" onclick="javascript: + window.location.href='index.php';"/> +</p> +</form> diff --git a/include/client/footer.inc.php b/include/client/footer.inc.php index 3398d1b93414fbdc978efb85e6d99d0893c95b18..bce8b7469c6192673e6a9bafcd9d73f020078eb5 100644 --- a/include/client/footer.inc.php +++ b/include/client/footer.inc.php @@ -1,13 +1,13 @@ </div> </div> <div id="footer"> - <p>Copyright © <?php echo date('Y'); ?> <a href="http://osticket.com" target="_blank" title="osTicket">osTicket.com</a> - All rights reserved.</p> - <a id="poweredBy" href="http://osticket.com" target="_blank">Powered by osTicket</a> + <p>Copyright © <?php echo date('Y'); ?> <?php echo (string) $ost->company ?: 'osTicket.com'; ?> - All rights reserved.</p> + <a id="poweredBy" href="http://osticket.com" target="_blank">Helpdesk software - powered by osTicket</a> </div> <div id="overlay"></div> <div id="loading"> <h4>Please Wait!</h4> <p>Please wait... it will take a second!</p> -</div> +</div> </body> -</html> +</html> diff --git a/include/client/header.inc.php b/include/client/header.inc.php index 9f20098b4b1b4fcfc2d891973717c42876296fde..107cf479c43884bf362b57f72378c40bc1a5be78 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -44,17 +44,18 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <p> <?php if($thisclient && is_object($thisclient) && $thisclient->isValid()) { - echo Format::htmlchars($thisclient->getName()).' - '; + echo Format::htmlchars($thisclient->getName()).' |'; ?> + <a href="<?php echo ROOT_PATH; ?>profile.php">Profile</a> | <?php if($cfg->showRelatedTickets()) {?> - <a href="<?php echo ROOT_PATH; ?>tickets.php">My Tickets <b>(<?php echo $thisclient->getNumTickets(); ?>)</b></a> - + <a href="<?php echo ROOT_PATH; ?>tickets.php">Tickets <b>(<?php echo $thisclient->getNumTickets(); ?>)</b></a> - <?php } ?> <a href="<?php echo ROOT_PATH; ?>logout.php?auth=<?php echo $ost->getLinkToken(); ?>">Log Out</a> <?php }elseif($nav){ ?> - Guest User - <a href="<?php echo ROOT_PATH; ?>login.php">Log In</a> + Guest User | <a href="<?php echo ROOT_PATH; ?>login.php">Log In</a> <?php } ?> </p> diff --git a/include/client/login.inc.php b/include/client/login.inc.php index 7e46c2eb30598e2220128654b0232cd08d81ac62..586ef9808b75144e37dafc775debb9cc8f4ca1c4 100644 --- a/include/client/login.inc.php +++ b/include/client/login.inc.php @@ -5,7 +5,8 @@ $email=Format::input($_POST['lemail']?$_POST['lemail']:$_GET['e']); $ticketid=Format::input($_POST['lticket']?$_POST['lticket']:$_GET['t']); ?> <h1>Check Ticket Status</h1> -<p>To view the status of a ticket, provide us with the login details below.</p> +<p>Please provide us with your email address and a ticket number, and an access +link will be emailed to you.</p> <form action="login.php" method="post" id="clientLogin"> <?php csrf_token(); ?> <strong><?php echo Format::htmlchars($errors['login']); ?></strong> @@ -15,14 +16,14 @@ $ticketid=Format::input($_POST['lticket']?$_POST['lticket']:$_GET['t']); <input id="email" type="text" name="lemail" size="30" value="<?php echo $email; ?>"> </div> <div> - <label for="ticketno">Ticket ID:</label> + <label for="ticketno">Ticket Number:</label> <input id="ticketno" type="text" name="lticket" size="16" value="<?php echo $ticketid; ?>"></td> </div> <p> - <input class="btn" type="submit" value="View Status"> + <input class="btn" type="submit" value="Email Access Link"> </p> </form> <br> <p> -If this is your first time contacting us or you've lost the ticket ID, please <a href="open.php">open a new ticket</a>. +If this is your first time contacting us or you've lost the ticket number, please <a href="open.php">open a new ticket</a>. </p> diff --git a/include/client/open.inc.php b/include/client/open.inc.php index 75211c0982b687475e41e2b9bc4e738aab1d1a7d..3f070c703d9c32b63fc43ca1f3001aff0207f546 100644 --- a/include/client/open.inc.php +++ b/include/client/open.inc.php @@ -4,7 +4,7 @@ $info=array(); if($thisclient && $thisclient->isValid()) { $info=array('name'=>$thisclient->getName(), 'email'=>$thisclient->getEmail(), - 'phone'=>$thisclient->getPhone()); + 'phone'=>$thisclient->getPhoneNumber()); } $info=($_POST && $errors)?Format::htmlchars($_POST):$info; diff --git a/include/client/profile.inc.php b/include/client/profile.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..a103ba1f7ec5f00ec1ec182b520ccfb2eaa8077b --- /dev/null +++ b/include/client/profile.inc.php @@ -0,0 +1,25 @@ +<?php + +?> +<h1>Manage Your Profile Information</h1> +<p> +Use the forms below to update the information we have on file for your +account +</p> +<form action="profile.php" method="post"> + <?php csrf_token(); ?> +<table width="800"> +<?php +foreach ($user->getForms() as $f) { + $f->render(false); +} +?> +</table> +<hr> +<p style="text-align: center;"> + <input type="submit" value="Update"/> + <input type="reset" value="Reset"/> + <input type="button" value="Cancel" onclick="javascript: + window.location.href='index.php';"/> +</p> +</form> diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php index 7c2c6e56436e5fcf454caf8c6dca5d117f4f6e72..1c227ba4cdec255b7e75607abfeab143e549d305 100644 --- a/include/client/tickets.inc.php +++ b/include/client/tickets.inc.php @@ -18,8 +18,8 @@ if(isset($_REQUEST['status'])) { //Query string status has nothing to do with th $status='open'; //Defaulting to open } -$sortOptions=array('id'=>'ticketID', 'name'=>'user.name', 'subject'=>'subject.value', - 'email'=>'email.address', 'status'=>'ticket.status', 'dept'=>'dept_name','date'=>'ticket.created'); +$sortOptions=array('id'=>'`number`', 'subject'=>'subject.value', + 'status'=>'ticket.status', 'dept'=>'dept_name','date'=>'ticket.created'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); //Sorting options... $order_by=$order=null; @@ -38,9 +38,8 @@ if($order_by && strpos($order_by,',')) $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$qselect='SELECT ticket.ticket_id,ticket.ticketID,ticket.dept_id,isanswered, ' - .'dept.ispublic, subject.value as subject, ' - .'user.name, email.address as email, ' +$qselect='SELECT ticket.ticket_id,ticket.`number`,ticket.dept_id,isanswered, ' + .'dept.ispublic, subject.value as subject,' .'dept_name,ticket. status, ticket.source, ticket.created '; $dynfields='(SELECT entry.object_id, value FROM '.FORM_ANSWER_TABLE.' ans '. @@ -51,11 +50,13 @@ $subject_sql = sprintf($dynfields, 'subject'); $qfrom='FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' - .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' - .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' + .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab + ON (collab.ticket_id = ticket.ticket_id + AND collab.user_id ='.$thisclient->getId().' )' .' LEFT JOIN '.$subject_sql.' subject ON ticket.ticket_id = subject.object_id '; -$qwhere =' WHERE email.address='.db_input($thisclient->getEmail()); +$qwhere = sprintf(' WHERE ( ticket.user_id=%d OR collab.user_id=%d )', + $thisclient->getId(), $thisclient->getId()); if($status){ $qwhere.=' AND ticket.status='.db_input($status); @@ -65,7 +66,7 @@ $search=($_REQUEST['a']=='search' && $_REQUEST['q']); if($search) { $qstr.='&a='.urlencode($_REQUEST['a']).'&q='.urlencode($_REQUEST['q']); if(is_numeric($_REQUEST['q'])) { - $qwhere.=" AND ticket.ticketID LIKE '$queryterm%'"; + $qwhere.=" AND ticket.`number` LIKE '$queryterm%'"; } else {//Deep search! $queryterm=db_real_escape($_REQUEST['q'],false); //escape the term ONLY...no quotes. $qwhere.=' AND ( ' @@ -100,7 +101,7 @@ if($search) $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting ?> -<h1>My Tickets</h1> +<h1>Tickets</h1> <br> <form action="tickets.php" method="get" id="ticketSearchForm"> <input type="hidden" name="a" value="search"> @@ -152,24 +153,24 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting if($row['attachments']) $subject.=' <span class="Icon file"></span>'; - $ticketID=$row['ticketID']; + $ticketNumber=$row['number']; if($row['isanswered'] && !strcasecmp($row['status'],'open')) { $subject="<b>$subject</b>"; - $ticketID="<b>$ticketID</b>"; + $ticketNumber="<b>$ticketNumber</b>"; } $phone=Format::phone($row['phone']); if($row['phone_ext']) $phone.=' '.$row['phone_ext']; ?> - <tr id="<?php echo $row['ticketID']; ?>"> + <tr id="<?php echo $row['ticket_id']; ?>"> <td class="centered"> <a class="Icon <?php echo strtolower($row['source']); ?>Ticket" title="<?php echo $row['email']; ?>" - href="tickets.php?id=<?php echo $row['ticketID']; ?>"><?php echo $ticketID; ?></a> + href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $ticketNumber; ?></a> </td> <td> <?php echo Format::db_date($row['created']); ?></td> <td> <?php echo ucfirst($row['status']); ?></td> <td> - <a href="tickets.php?id=<?php echo $row['ticketID']; ?>"><?php echo $subject; ?></a> + <a href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $subject; ?></a> </td> <td> <?php echo Format::truncate($dept,30); ?></td> <td><?php echo $phone; ?></td> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index 7828e46617ac8264979cb8ed925f2cfacd99fc26..469740f6c7e60d1c434d8b1d9709c4c27fcfe89e 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkClientAccess($thisclient)) die('Access Denied!'); +if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkUserAccess($thisclient)) die('Access Denied!'); $info=($_POST && $errors)?Format::htmlchars($_POST):array(); @@ -13,8 +13,12 @@ if(!$dept || !$dept->isPublic()) <tr> <td colspan="2" width="100%"> <h1> - Ticket #<?php echo $ticket->getExtId(); ?> - <a href="view.php?id=<?php echo $ticket->getExtId(); ?>" title="Reload"><span class="Icon refresh"> </span></a> + Ticket #<?php echo $ticket->getNumber(); ?> + <a href="view.php?id=<?php echo $ticket->getId(); ?>" title="Reload"><span class="Icon refresh"> </span></a> +<?php if ($cfg->allowClientUpdates()) { ?> + <a class="action-button" href="tickets.php?a=edit&id=<?php + echo $ticket->getId(); ?>"><i class="icon-edit"></i> Edit</a> +<?php } ?> </h1> </td> </tr> @@ -124,10 +128,10 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) { <?php }elseif($warn) { ?> <div id="msg_warning"><?php echo $warn; ?></div> <?php } ?> -<form id="reply" action="tickets.php?id=<?php echo $ticket->getExtId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data"> +<form id="reply" action="tickets.php?id=<?php echo $ticket->getId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <h2>Post a Reply</h2> - <input type="hidden" name="id" value="<?php echo $ticket->getExtId(); ?>"> + <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> <input type="hidden" name="a" value="reply"> <table border="0" cellspacing="0" cellpadding="3" style="width:100%"> <tr> @@ -143,7 +147,7 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) { <br/> <textarea name="message" id="message" cols="50" rows="9" wrap="soft" data-draft-namespace="ticket.client" - data-draft-object-id="<?php echo $ticket->getExtId(); ?>" + data-draft-object-id="<?php echo $ticket->getId(); ?>" class="richtext ifhtml draft"><?php echo $info['message']; ?></textarea> </td> </tr> diff --git a/include/html2text.php b/include/html2text.php index 48b0b3ee1689b44592347e1955dad086904baa9f..0c1ec08330d8a8c77acd80d1f187d6b6c0d7d4a4 100644 --- a/include/html2text.php +++ b/include/html2text.php @@ -218,8 +218,9 @@ class HtmlInlineElement { } if ($this->footnotes) { $output .= "\n\n" . str_repeat('-', $width/2) . "\n"; + $id = 1; foreach ($this->footnotes as $name=>$content) - $output .= "[$name] ".$content."\n"; + $output .= sprintf("[%d] %s\n", $id++, $content); } return $output; } @@ -276,7 +277,8 @@ class HtmlInlineElement { } function addFootNote($name, $content) { - $this->footnotes[$name] = $content; + $this->footnotes[$content] = $content; + return count($this->footnotes); } } @@ -447,8 +449,8 @@ class HtmlAElement extends HtmlInlineElement { $output = (($href != $output) ? "$href " : '') . "<$output>"; } elseif (mb_strwidth($href) > $width / 2) { if ($href != $output) - $this->getRoot()->addFootnote($output, $href); - $output = "[$output]"; + $id = $this->getRoot()->addFootnote($output, $href); + $output = "[$output][$id]"; } elseif ($href != $output) { $output = "[$output]($href)"; } diff --git a/include/i18n/en_US/file.yaml b/include/i18n/en_US/file.yaml index 19f2e35a5a04618e8b08a015961581f82634937c..b6b9ac906fe09b8a61aa32c1c07f46138cc811ee 100644 --- a/include/i18n/en_US/file.yaml +++ b/include/i18n/en_US/file.yaml @@ -9,7 +9,7 @@ # bill be cleaned up shortly after installation (by the autocron). # --- -- hash: b56944cb4722cc5cda9d1e23a3ea7fbc +- key: b56944cb4722cc5cda9d1e23a3ea7fbc name: powered-by-osticket.png type: image/png encoding: base64 @@ -105,7 +105,7 @@ NQLJLj4m+f1lr/Kfzeo1TMm+prKzxxgz2AeQEd49EE0PDxwVGTx+lDbX4G94cZ021zwTueT9+79q vjLYs5AR3j0Seq638S2PileedN26W3OuWb8rz9FmsJfg/wBsHf7rZZG4/wAAAABJRU5ErkJggg== -- hash: 6fe1efdea357534d238b86e7860a7c5a +- key: 6fe1efdea357534d238b86e7860a7c5a name: kangaroo.png type: image/png encoding: base64 diff --git a/include/i18n/en_US/group.yaml b/include/i18n/en_US/group.yaml index 942222cbd1a04c4cc883323fd34dfd555f6ed054..019aa75ef1aef197418c305060e92123a43784ff 100644 --- a/include/i18n/en_US/group.yaml +++ b/include/i18n/en_US/group.yaml @@ -67,7 +67,7 @@ depts: [1, 2, 3] -- isactve: 1 +- isactive: 1 name: Staff notes: | Lowly staff members diff --git a/include/i18n/en_US/help/tips/install.yaml b/include/i18n/en_US/help/tips/install.yaml index 2cb86b36dfeee5b7a282c2e797f4fd992fa06234..b65340da0096ba4152e4660bbc639b058a3b340f 100644 --- a/include/i18n/en_US/help/tips/install.yaml +++ b/include/i18n/en_US/help/tips/install.yaml @@ -20,6 +20,16 @@ system_email: <p>Default email address e.g support@yourcompany.com - you can add more later!</p> +default_lang: + title: Default System Language + content: | + <p>Initial data for this language will be installed into the + database. For instance email templates and default system pages will + be installed for this language.</p> + links: + - title: osTicket Language Packs + href: http://osticket.com/download?product=langs + first_name: title: First Name content: | diff --git a/include/i18n/en_US/help/tips/settings.autoresponder.yaml b/include/i18n/en_US/help/tips/settings.autoresponder.yaml index 89f0d6faf51216be6524299bbc8fe7711cb894c1..8f764de6f48595dfa281c26a3542b52106d8a9b4 100644 --- a/include/i18n/en_US/help/tips/settings.autoresponder.yaml +++ b/include/i18n/en_US/help/tips/settings.autoresponder.yaml @@ -34,17 +34,28 @@ new_staff_ticket: new_message: title: New Message Confirmation content: > - Confirmation notice sent when a new message is appended to an - existing ticket + Send a confirmation receipt to the message submitter when a new + message is appended to an existing ticket. links: - title: Default New Message Confirmation Template href: /scp/templates.php?default_for=message.autoresp +collaborators: + title: New Activity Notice + content: > + Broadcast messages received from message submitter to all other + participants on the ticket. + overlimit_notice: title: Overlimit Notice content: > - Ticket denied notice sent to user on limit violation. Admin gets - alerts on <em>ALL</em> denials by default + Send ticket denied notice to <offending> user when the <em>maximum + open tickets</em> limit is reached. Admin gets alerts on + <em>ALL</em> denials by default links: - title: Default Overlimit Notice Template href: /scp/templates.php?default_for=ticket.overlimit + + - title: Set <em>Maximum Open Tickets</em> + href: /scp/settings?t=tickets + diff --git a/include/i18n/en_US/help/tips/settings.email.yaml b/include/i18n/en_US/help/tips/settings.email.yaml index fa3ddc043919d4de30038208d9eb588097b7f39b..ce58aa7999059ec07ade6244875616ed0ab8dc59 100644 --- a/include/i18n/en_US/help/tips/settings.email.yaml +++ b/include/i18n/en_US/help/tips/settings.email.yaml @@ -81,3 +81,19 @@ enable_autocron_poll: periodically run cleanup routines called the auto-cron. This setting enables fetching email during this cleanup. +use_email_priority: + title: Honor Email Priority + content: > + Honor the priority flag set in incoming email. If enabled, the + priority of new tickets will be set by the priority of the email + used to create them, if available. + +add_email_collabs: + title: Email Collaborators + content: > + Add participants included in the <code><strong>To</strong></code> and + <code><strong>Cc</strong></code> fields of email as ticket + collaborators<br> + <br> + <em>Collaborators can always be added manually by staff members when + viewing a ticket.</em> diff --git a/include/i18n/en_US/templates/email/message.alert.yaml b/include/i18n/en_US/templates/email/message.alert.yaml index b3fb1686f3717144c5c956d2676d9bbd31d90502..e6e83e5bbd5f076cd85d4a0b2336eaec2a919189 100644 --- a/include/i18n/en_US/templates/email/message.alert.yaml +++ b/include/i18n/en_US/templates/email/message.alert.yaml @@ -14,7 +14,7 @@ notes: | subject: | New Message Alert body: | - <h3><strong>Hi %{recipient},</strong></h3> + <h3><strong>Hi %{recipient.name},</strong></h3> New message appended to ticket <a href="%{ticket.staff_link}">#%{ticket.number}</a> <br> diff --git a/include/i18n/en_US/templates/email/message.autoresp.yaml b/include/i18n/en_US/templates/email/message.autoresp.yaml index 30c641de0f74048f297333de499197865c7a2dbd..ec8c6fb521c4875c014a0ec4f4707b7bac6f72da 100644 --- a/include/i18n/en_US/templates/email/message.autoresp.yaml +++ b/include/i18n/en_US/templates/email/message.autoresp.yaml @@ -14,9 +14,9 @@ notes: | subject: | Message Confirmation body: | - <h3><strong>Dear %{ticket.name.first},</strong></h3> + <h3><strong>Dear %{recipient.name.first},</strong></h3> Your reply to support request <a - href="%{ticket.client_link}">#%{ticket.number}</a> has been noted + href="%{recipient.ticket_link}">#%{ticket.number}</a> has been noted <br> <br> <div style="color: rgb(127, 127, 127); "> @@ -26,7 +26,5 @@ body: | <hr> <div style="color: rgb(127, 127, 127); font-size: small; text-align: center"><em>You can view the support request progress <a - href="%{ticket.client_link}">online here</a></em><br> - <a href="http://osticket.com/"><img - src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" alt="Powered by osTicket" - width="126" height="19" style="width: 126px;"></a></div> + href="%{recipient.ticket_link}">online here</a></em> + </div> diff --git a/include/i18n/en_US/templates/email/note.alert.yaml b/include/i18n/en_US/templates/email/note.alert.yaml index 32ccf501836349be9c8ee8a08c6495a87b095056..a704830d6bb3300607be5b3c7f0c6a55741b4955 100644 --- a/include/i18n/en_US/templates/email/note.alert.yaml +++ b/include/i18n/en_US/templates/email/note.alert.yaml @@ -12,7 +12,7 @@ notes: | subject: | New Internal Note Alert body: | - <h3><strong>Hi %{recipient},</strong></h3> + <h3><strong>Hi %{recipient.name},</strong></h3> An internal note has been appended to ticket <a href="%{ticket.staff_link}">#%{ticket.number}</a> <br> diff --git a/include/i18n/en_US/templates/email/ticket.activity.notice.yaml b/include/i18n/en_US/templates/email/ticket.activity.notice.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d7baebed6f78472941a67b72d29c9d959b5136fb --- /dev/null +++ b/include/i18n/en_US/templates/email/ticket.activity.notice.yaml @@ -0,0 +1,28 @@ +# +# Email template: ticket.activity.notice.yaml +# +# Notice sent to collaborators on ticket activity e.g reply or message +# +--- +notes: | + Notice sent to collaborators on ticket activity e.g reply or message. + +subject: | + Re: %{ticket.subject} [#%{ticket.number}] +body: | + <h3><strong>Dear %{recipient.name.first},</strong></h3> + <div> + <em>%{poster.name}</em> just logged a message to a ticket in which you participate. + </div> + <br> + %{message} + <br> + <br> + <hr> + <div style="color: rgb(127, 127, 127); font-size: small; text-align: center;"> + <em>You're getting this email because you are a collaborator + on ticket <a href="%{recipient.ticket_link}" style="color: rgb(84, 141, 212);" + >#%{ticket.number}</a>. To participate, simply reply to this email + or <a href="%{recipient.ticket_link}" style="color: rgb(84, 141, 212);" + >click here</a> for a complete archive of the ticket thread.</em> + </div> diff --git a/include/i18n/en_US/templates/email/ticket.alert.yaml b/include/i18n/en_US/templates/email/ticket.alert.yaml index 497064134e9bd520a188c9cad46849b21dcbf0a9..764a27bea07e85fc386c7508f9abfaae5633591c 100644 --- a/include/i18n/en_US/templates/email/ticket.alert.yaml +++ b/include/i18n/en_US/templates/email/ticket.alert.yaml @@ -12,7 +12,7 @@ notes: | subject: | New Ticket Alert body: | - <h2>Hi %{recipient},</h2> + <h2>Hi %{recipient.name},</h2> New ticket #%{ticket.number} created <br> <br> diff --git a/include/i18n/en_US/templates/email/ticket.autoreply.yaml b/include/i18n/en_US/templates/email/ticket.autoreply.yaml index e50551566ac31ec72311dcd584fe70bde5bfc467..39e8e4aa3fc7a1e56f1903096d5fa7a7a91b0cd9 100644 --- a/include/i18n/en_US/templates/email/ticket.autoreply.yaml +++ b/include/i18n/en_US/templates/email/ticket.autoreply.yaml @@ -12,20 +12,11 @@ notes: | Available variables for replacement: %{ticket.*}, %{response} subject: | - Support Ticket Opened + Re: %{ticket.subject} [#%{ticket.number}] body: | - <span style="color: rgb(127, 127, 127); font-family: Georgia; - font-size: 28pt; font-weight: normal;">We Hear You</span><img - src="cid:6fe1efdea357534d238b86e7860a7c5a" width="96" - alt="osTicket Logo (kangaroo)" height="54" style="width: 96px; - float: right; margin: 0px 0px 10px 10px;"> - <br> - <br> - <strong>Dear %{ticket.name.first},</strong> - <br> - <br> + <h3><strong>Dear %{recipient.name.first},</strong></h3> A request for support has been created and assigned ticket <a - href="%{ticket.client_link}">#%{ticket.number}</a> with the following + href="%{recipient.ticket_link}">#%{ticket.number}</a> with the following automatic reply <br> <br> @@ -40,14 +31,9 @@ body: | <div style="color: rgb(127, 127, 127);">Your %{company.name} Team,<br> %{signature}</div> <hr> - <div style="color: rgb(127, 127, 127); font-size: small; text-align: - center;"><em>We hope this response has sufficiently answered your questions. - If not, please do not send another email. Instead, reply to this email - or <a href="%{ticket.client_link}"><span style="color: rgb(84, 141, - 212);" >login to your account</span></a> for a complete archive of all - your support requests and responses.</em></div> - <div style="text-align: center;"> - <a href="http://osticket.com/"><img - src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" width="126" alt="Powered - by osTicket" height="19" style="width: 126px;"/></a> - </div> + <div style="color: rgb(127, 127, 127); font-size: small;"><em>We hope + this response has sufficiently answered your questions. If you wish to + provide additional comments or informatione, please reply to this email + or <a href="%{recipient.ticket_link}"><span + style="color: rgb(84, 141, 212);" >login to your account</span></a> for + a complete archive of your support requests.</em></div> diff --git a/include/i18n/en_US/templates/email/ticket.autoresp.yaml b/include/i18n/en_US/templates/email/ticket.autoresp.yaml index ebdfde6f106bed221ff5ebe44af66f4456dea58f..6b1826e9e4cfc8201499da64422fc1004b3b1d63 100644 --- a/include/i18n/en_US/templates/email/ticket.autoresp.yaml +++ b/include/i18n/en_US/templates/email/ticket.autoresp.yaml @@ -8,66 +8,33 @@ notes: | Sent to a user when a new ticket is created subject: | - Support Ticket Opened + Support Ticket Opened [#%{ticket.number}] body: | - <table cellspacing="0"> - <tbody> - <tr> - <td style="vertical-align: middle; border-bottom: 1px solid #ddd; height: 48pt"> - <span style="color: rgb(127, 127, 127); font-family: Georgia; - font-size: 28pt; font-weight: normal; ">We Hear You</span> - </td> - <td style="border-bottom: 1px solid #ddd;"> - <img src="cid:6fe1efdea357534d238b86e7860a7c5a" width="94" - alt="osTicket Logo (kangaroo)" style="width: 94px; float: right; - vertical-align: bottom; " height="53"> - </td> - </tr> - <tr> - <td style="width:67%; padding-top: 12pt; padding-right: 12pt"> - Dear %{ticket.name.first}, - <br> - <br> - We received your request and assigned ticket #%{ticket.number} - <br> - <br> - Topic: <strong>%{ticket.topic.name}</strong> - <br> - Subject: <strong>%{ticket.subject}</strong> - <br> - Submitted: <strong>%{ticket.create_date}</strong> - <br> - <br> - A representative will follow-up with you as soon as possible. - You can <a href="%{ticket.client_link}">view this ticket's - progress online</a>. - <br> - <br> - Your %{company.name} Team,<br> - %{signature} - </td> - <td style="vertical-align: top; padding-top: 12pt;"> - <span style="color: rgb(127, 127, 127); ">%{company.name}<br/> - %{signature}</span> - <br> - <br> - <span style="color: rgb(127, 127, 127); "> If you wish to send - additional comments or information regarding this issue, please - don't open a new ticket. Simply</span> <a - href="%{ticket.client_link}"><span style="color: rgb(84, 141, 212);" - >login</span></a> <span style="color: rgb(127, 127, 127);" - >and update the ticket.</span> - <br> - <br> - <span style="color: rgb(127, 127, 127); ">Visit our</span> <a - href="%{url}/kb"><span style="color: rgb(84, 141, 212); - ">knowledgebase</span></a> - </td> - </tr> - </tbody> - </table> - <div style="text-align: center;"> - <a href="http://osticket.com/"><img - src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" width="126" height="19" - style="width: 126px; " alt="Powered by osTicket"></a> + <h3><strong>Dear %{recipient.name.first},</strong></h3> + Our customer care team has created a ticket, + <a href="%{recipient.ticket_link}">#%{ticket.number}</a> on your behalf, + with the following details and summary: <br> + <br> + Subject: <strong>%{ticket.subject}</strong><br> + Submitted: <strong>%{ticket.create_date}</strong> + <br> + <br> + %{message} + <br> + <br> + A representative will follow-up with you as soon as possible. You can + <a href="%{recipient.ticket_link}">view this ticket's progress + online</a>. + <br> + <br> + <div style="color: rgb(127, 127, 127)"> + Your %{company.name} Team, + <br> + %{signature} </div> + <hr> + <div style="color: rgb(127, 127, 127); font-size: small; "><em>If you + wish to provide additional comments or information regarding the issue, + please reply to this email or <a href="%{recipient.ticket_link}"><span + style="color: rgb(84, 141, 212);" >login to your account</span></a> for + a complete archive of your support requests.</em></div> diff --git a/include/i18n/en_US/templates/email/ticket.notice.yaml b/include/i18n/en_US/templates/email/ticket.notice.yaml index 530f32a1f78fb33bab55b6b7b2942ffdaa798d73..bf2a6803c92eb299792264774f933620dc8f9d5c 100644 --- a/include/i18n/en_US/templates/email/ticket.notice.yaml +++ b/include/i18n/en_US/templates/email/ticket.notice.yaml @@ -10,20 +10,11 @@ notes: | This is most commonly performed when user's call in on the phone. subject: | - %{ticket.subject} + %{ticket.subject} [#%{ticket.number}] body: | - <span style="color: rgb(127, 127, 127); font-family: Georgia; - font-size: 28pt; font-weight: normal;">We're Here For - You</span><img src="cid:6fe1efdea357534d238b86e7860a7c5a" - width="96" alt="osTicket Logo (kangaroo)" height="54" style="width: 96px; - float: right; margin: 0px 0px 10px 10px;"> - <br> - <br> - <strong>Dear %{ticket.name.first},</strong> - <br> - <br> + <h3><strong>Dear %{recipient.name.first},</strong></h3> Our customer care team has created a ticket, <a - href="%{ticket.client_link}">#%{ticket.number}</a> on your behalf, with + href="%{recipient.ticket_link}">#%{ticket.number}</a> on your behalf, with the following details and summary: <br> <br> @@ -36,7 +27,7 @@ body: | <br> <br> If need be, a representative will follow-up with you as soon as - possible. You can also <a href="%{ticket.client_link}">view this + possible. You can also <a href="%{recipient.ticket_link}">view this ticket's progress online</a>. <br> <br> @@ -46,12 +37,6 @@ body: | <hr> <div style="color: rgb(127, 127, 127); font-size: small; "><em>If you wish to provide additional comments or information regarding the issue, - please do not send another email. Instead, reply to this email or <a - href="%{ticket.client_link}"><span style="color: rgb(84, 141, 212);" - >login to your account</span></a> for a complete archive of all your - support requests and responses.</em></div> - <br> - <div style="text-align: center;"> - <img src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" width="126" - alt="Powered by osTicket" height="19" style="width: 126px;"> - </div> + please reply to this email or <a href="%{recipient.ticket_link}"><span + style="color: rgb(84, 141, 212);" >login to your account</span></a> for + a complete archive of your support requests.</em></div> diff --git a/include/i18n/en_US/templates/email/ticket.overdue.yaml b/include/i18n/en_US/templates/email/ticket.overdue.yaml index d0a84da1caed8d6d297e512f7e803558765f0f8d..5f165fa739ab2bebbbe4bb0ad35e7546780183e8 100644 --- a/include/i18n/en_US/templates/email/ticket.overdue.yaml +++ b/include/i18n/en_US/templates/email/ticket.overdue.yaml @@ -14,7 +14,7 @@ notes: | subject: | Stale Ticket Alert body: | - <h3><strong>Hi %{recipient}</strong>,</h3> + <h3><strong>Hi %{recipient.name}</strong>,</h3> A ticket, <a href="%{ticket.staff_link}">#%{ticket.number}</a> is seriously overdue. <br> diff --git a/include/i18n/en_US/templates/email/ticket.overlimit.yaml b/include/i18n/en_US/templates/email/ticket.overlimit.yaml index 384f329cc6b997982bf6eef26e0da3357ef13e3c..724e3a8e0b41b4f22835c56d8ebd60494f18507e 100644 --- a/include/i18n/en_US/templates/email/ticket.overlimit.yaml +++ b/include/i18n/en_US/templates/email/ticket.overlimit.yaml @@ -14,15 +14,7 @@ notes: | subject: | Open Tickets Limit Reached body: | - <span style="font-family: Georgia; color: rgb(127, 127, 127); font-size: 30pt;"> - We Hear You</span> - <img src="cid:6fe1efdea357534d238b86e7860a7c5a" alt="osTicket Logo (kangaroo)" - width="99" height="56" style="float: right; width: 99px; margin: 0px 0px 10px 10px;"> - <br> - <br> - <strong>Dear %{ticket.name.first},</strong> - <br> - <br> + <h3><strong>Dear %{ticket.name.first},</strong></h3> You have reached the maximum number of open tickets allowed. To be able to open another ticket, one of your pending tickets must be closed. To update or add comments to an open ticket simply <a @@ -31,9 +23,3 @@ body: | <br> Thank you,<br/> Support Ticket System - <br> - <br> - <div style="text-align: center;"> - <img src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" - alt="Powered by osTicket" width="126" height="19" style="width: 126px;"> - </div> diff --git a/include/i18n/en_US/templates/email/ticket.reply.yaml b/include/i18n/en_US/templates/email/ticket.reply.yaml index c576a720945eb7b490132aa8d31c30d468da8a17..1996f6f73075d11e72f5e8830185e0145bfed8d9 100644 --- a/include/i18n/en_US/templates/email/ticket.reply.yaml +++ b/include/i18n/en_US/templates/email/ticket.reply.yaml @@ -9,15 +9,10 @@ notes: | Sent to users when a staff members makes a reply to their ticket. Replies are only generated by staff members. -subject: "Re: %{ticket.subject}" +subject: | + Re: %{ticket.subject} [#%{ticket.number}] body: | - <img src="cid:6fe1efdea357534d238b86e7860a7c5a" alt="osTicket Logo (kangaroo)" - width="113" height="64" style="float: right; width: 113px; margin: 0px 0px 10px 10px;"> - <h3><span style="color: rgb(127, 127, 127); font-weight: normal; - font-family: Georgia; font-size: 30pt">We're Here For You</span></h3> - <strong>Dear %{ticket.name},</strong> - <br> - <br> + <h3><strong>Dear %{recipient.name},</strong></h3> %{response} <br> <br> @@ -29,11 +24,6 @@ body: | <div style="color: rgb(127, 127, 127); font-size: small; text-align: center;" ><em>We hope this response has sufficiently answered your questions. If not, please do not send another email. Instead, reply to this email or - <a href="%{ticket.client_link}" style="color: rgb(84, 141, 212);" >login + <a href="%{recipient.ticket_link}" style="color: rgb(84, 141, 212);" >login to your account</a> for a complete archive of all your support requests and responses.</em></div> - <div style="text-align: center;"> - <a href="http://osticket.com/"><img - src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" alt="Powered by osTicket" - width="126" height="19" style="width: 126px;"></a> - </div> diff --git a/include/i18n/en_US/templates/email/transfer.alert.yaml b/include/i18n/en_US/templates/email/transfer.alert.yaml index 4772aefd19adb51ab76aad258024518965792fd2..9bdec78bdda6fb235610827ec38a0ce33daa1f0e 100644 --- a/include/i18n/en_US/templates/email/transfer.alert.yaml +++ b/include/i18n/en_US/templates/email/transfer.alert.yaml @@ -11,7 +11,7 @@ notes: | subject: | Ticket #%{ticket.number} transfer - %{ticket.dept.name} body: | - <h3>Hi %{recipient},</h3> + <h3>Hi %{recipient.name},</h3> Ticket <a href="%{ticket.staff_link}">#%{ticket.number}</a> has been transferred to the %{ticket.dept.name} department by <strong>%{staff.name.short}</strong> diff --git a/include/i18n/en_US/templates/email/user.accesslink.yaml b/include/i18n/en_US/templates/email/user.accesslink.yaml new file mode 100644 index 0000000000000000000000000000000000000000..acd50fe0fd5b9c89b9efffffa72e271b85491f0e --- /dev/null +++ b/include/i18n/en_US/templates/email/user.accesslink.yaml @@ -0,0 +1,32 @@ +# +# Email template: user.accesslink +# +# Sent when a user requests an access link to check the status of a ticket +# +# +# +--- +notes: | + Sent when a user requests an access link to check the status of a ticket + +subject: | + Ticket [#%{ticket.number}] Access Link +body: | + <h3><strong>Hi %{recipient.name.first},</strong></h3> + An access link request for ticket #%{ticket.number} has been submitted on your behalf for the + helpdesk at %{url}. + <br> + <br> + Follow the link below to check the status of the ticket #%{ticket.number}. + <br> + <br> + <a href="%{recipient.ticket_link}">%{recipient.ticket_link}</a> + <br> + <br>If you <strong>did not</strong> make the request, please delete + and disregard this email. Your account is still secure and no one has + been given access to the ticket. Someone could have mistakenly entered + your email address. + <br> + <br> + --<br> + %{company.name} diff --git a/include/i18n/langs.php b/include/i18n/langs.php new file mode 100644 index 0000000000000000000000000000000000000000..09957c42be514963c8d7a40f99051b996e728b42 --- /dev/null +++ b/include/i18n/langs.php @@ -0,0 +1,735 @@ +<?php +/** + * @author Phil Teare + * using wikipedia data + */ +return array( + "ab" => array( + "name" => "Abkhaz", + "nativeName" => "аҧÑуа" + ), + "aa" => array( + "name" => "Afar", + "nativeName" => "Afaraf" + ), + "af" => array( + "name" => "Afrikaans", + "nativeName" => "Afrikaans" + ), + "ak" => array( + "name" => "Akan", + "nativeName" => "Akan" + ), + "sq" => array( + "name" => "Albanian", + "nativeName" => "Shqip" + ), + "am" => array( + "name" => "Amharic", + "nativeName" => "አማáˆáŠ›" + ), + "ar" => array( + "name" => "Arabic", + "nativeName" => "العربية" + ), + "an" => array( + "name" => "Aragonese", + "nativeName" => "Aragonés" + ), + "hy" => array( + "name" => "Armenian", + "nativeName" => "Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶" + ), + "as" => array( + "name" => "Assamese", + "nativeName" => "অসমীয়া" + ), + "av" => array( + "name" => "Avaric", + "nativeName" => "авар мацӀ, магӀарул мацӀ" + ), + "ae" => array( + "name" => "Avestan", + "nativeName" => "avesta" + ), + "ay" => array( + "name" => "Aymara", + "nativeName" => "aymar aru" + ), + "az" => array( + "name" => "Azerbaijani", + "nativeName" => "azÉ™rbaycan dili" + ), + "bm" => array( + "name" => "Bambara", + "nativeName" => "bamanankan" + ), + "ba" => array( + "name" => "Bashkir", + "nativeName" => "башҡорт теле" + ), + "eu" => array( + "name" => "Basque", + "nativeName" => "euskara, euskera" + ), + "be" => array( + "name" => "Belarusian", + "nativeName" => "БеларуÑкаÑ" + ), + "bn" => array( + "name" => "Bengali", + "nativeName" => "বাংলা" + ), + "bh" => array( + "name" => "Bihari", + "nativeName" => "à¤à¥‹à¤œà¤ªà¥à¤°à¥€" + ), + "bi" => array( + "name" => "Bislama", + "nativeName" => "Bislama" + ), + "bs" => array( + "name" => "Bosnian", + "nativeName" => "bosanski jezik" + ), + "br" => array( + "name" => "Breton", + "nativeName" => "brezhoneg" + ), + "bg" => array( + "name" => "Bulgarian", + "nativeName" => "българÑки език" + ), + "my" => array( + "name" => "Burmese", + "nativeName" => "ဗမာစာ" + ), + "ca" => array( + "name" => "Catalan; Valencian", + "nativeName" => "Català " + ), + "ch" => array( + "name" => "Chamorro", + "nativeName" => "Chamoru" + ), + "ce" => array( + "name" => "Chechen", + "nativeName" => "нохчийн мотт" + ), + "ny" => array( + "name" => "Chichewa; Chewa; Nyanja", + "nativeName" => "chiCheŵa, chinyanja" + ), + "zh" => array( + "name" => "Chinese", + "nativeName" => "䏿–‡ (ZhÅngwén), 汉è¯, 漢語" + ), + "cv" => array( + "name" => "Chuvash", + "nativeName" => "чӑваш чӗлхи" + ), + "kw" => array( + "name" => "Cornish", + "nativeName" => "Kernewek" + ), + "co" => array( + "name" => "Corsican", + "nativeName" => "corsu, lingua corsa" + ), + "cr" => array( + "name" => "Cree", + "nativeName" => "á“€á¦áƒá”ááá£" + ), + "hr" => array( + "name" => "Croatian", + "nativeName" => "hrvatski" + ), + "cs" => array( + "name" => "Czech", + "nativeName" => "Äesky, ÄeÅ¡tina" + ), + "da" => array( + "name" => "Danish", + "nativeName" => "dansk" + ), + "dv" => array( + "name" => "Divehi; Dhivehi; Maldivian;", + "nativeName" => "Þ‹Þ¨ÞˆÞ¬Þ€Þ¨" + ), + "nl" => array( + "name" => "Dutch", + "nativeName" => "Nederlands, Vlaams" + ), + "en" => array( + "name" => "English", + "nativeName" => "English" + ), + "eo" => array( + "name" => "Esperanto", + "nativeName" => "Esperanto" + ), + "et" => array( + "name" => "Estonian", + "nativeName" => "eesti, eesti keel" + ), + "ee" => array( + "name" => "Ewe", + "nativeName" => "EÊ‹egbe" + ), + "fo" => array( + "name" => "Faroese", + "nativeName" => "føroyskt" + ), + "fj" => array( + "name" => "Fijian", + "nativeName" => "vosa Vakaviti" + ), + "fi" => array( + "name" => "Finnish", + "nativeName" => "suomi, suomen kieli" + ), + "fr" => array( + "name" => "French", + "nativeName" => "français, langue française" + ), + "ff" => array( + "name" => "Fula; Fulah; Pulaar; Pular", + "nativeName" => "Fulfulde, Pulaar, Pular" + ), + "gl" => array( + "name" => "Galician", + "nativeName" => "Galego" + ), + "ka" => array( + "name" => "Georgian", + "nativeName" => "ქáƒáƒ თული" + ), + "de" => array( + "name" => "German", + "nativeName" => "Deutsch" + ), + "el" => array( + "name" => "Greek, Modern", + "nativeName" => "Ελληνικά" + ), + "gn" => array( + "name" => "GuaranÃ", + "nativeName" => "Avañeẽ" + ), + "gu" => array( + "name" => "Gujarati", + "nativeName" => "ગà«àªœàª°àª¾àª¤à«€" + ), + "ht" => array( + "name" => "Haitian; Haitian Creole", + "nativeName" => "Kreyòl ayisyen" + ), + "ha" => array( + "name" => "Hausa", + "nativeName" => "Hausa, Ù‡ÙŽÙˆÙØ³ÙŽ" + ), + "he" => array( + "name" => "Hebrew (modern)", + "nativeName" => "עברית" + ), + "hz" => array( + "name" => "Herero", + "nativeName" => "Otjiherero" + ), + "hi" => array( + "name" => "Hindi", + "nativeName" => "हिनà¥à¤¦à¥€, हिंदी" + ), + "ho" => array( + "name" => "Hiri Motu", + "nativeName" => "Hiri Motu" + ), + "hu" => array( + "name" => "Hungarian", + "nativeName" => "Magyar" + ), + "ia" => array( + "name" => "Interlingua", + "nativeName" => "Interlingua" + ), + "id" => array( + "name" => "Indonesian", + "nativeName" => "Bahasa Indonesia" + ), + "ie" => array( + "name" => "Interlingue", + "nativeName" => "Originally called Occidental; then Interlingue after WWII" + ), + "ga" => array( + "name" => "Irish", + "nativeName" => "Gaeilge" + ), + "ig" => array( + "name" => "Igbo", + "nativeName" => "Asụsụ Igbo" + ), + "ik" => array( + "name" => "Inupiaq", + "nativeName" => "Iñupiaq, Iñupiatun" + ), + "io" => array( + "name" => "Ido", + "nativeName" => "Ido" + ), + "is" => array( + "name" => "Icelandic", + "nativeName" => "Ãslenska" + ), + "it" => array( + "name" => "Italian", + "nativeName" => "Italiano" + ), + "iu" => array( + "name" => "Inuktitut", + "nativeName" => "áƒá“„ᒃᑎá‘ᑦ" + ), + "ja" => array( + "name" => "Japanese", + "nativeName" => "日本語 (ã«ã»ã‚“ã”ï¼ã«ã£ã½ã‚“ã”)" + ), + "jv" => array( + "name" => "Javanese", + "nativeName" => "basa Jawa" + ), + "kl" => array( + "name" => "Kalaallisut, Greenlandic", + "nativeName" => "kalaallisut, kalaallit oqaasii" + ), + "kn" => array( + "name" => "Kannada", + "nativeName" => "ಕನà³à²¨à²¡" + ), + "kr" => array( + "name" => "Kanuri", + "nativeName" => "Kanuri" + ), + "ks" => array( + "name" => "Kashmiri", + "nativeName" => "कशà¥à¤®à¥€à¤°à¥€, كشميري‎" + ), + "kk" => array( + "name" => "Kazakh", + "nativeName" => "Қазақ тілі" + ), + "km" => array( + "name" => "Khmer", + "nativeName" => "ភាសាážáŸ’មែរ" + ), + "ki" => array( + "name" => "Kikuyu, Gikuyu", + "nativeName" => "GÄ©kÅ©yÅ©" + ), + "rw" => array( + "name" => "Kinyarwanda", + "nativeName" => "Ikinyarwanda" + ), + "ky" => array( + "name" => "Kirghiz, Kyrgyz", + "nativeName" => "кыргыз тили" + ), + "kv" => array( + "name" => "Komi", + "nativeName" => "коми кыв" + ), + "kg" => array( + "name" => "Kongo", + "nativeName" => "KiKongo" + ), + "ko" => array( + "name" => "Korean", + "nativeName" => "한êµì–´ (韓國語), ì¡°ì„ ë§ (æœé®®èªž)" + ), + "ku" => array( + "name" => "Kurdish", + "nativeName" => "Kurdî, كوردی‎" + ), + "kj" => array( + "name" => "Kwanyama, Kuanyama", + "nativeName" => "Kuanyama" + ), + "la" => array( + "name" => "Latin", + "nativeName" => "latine, lingua latina" + ), + "lb" => array( + "name" => "Luxembourgish, Letzeburgesch", + "nativeName" => "Lëtzebuergesch" + ), + "lg" => array( + "name" => "Luganda", + "nativeName" => "Luganda" + ), + "li" => array( + "name" => "Limburgish, Limburgan, Limburger", + "nativeName" => "Limburgs" + ), + "ln" => array( + "name" => "Lingala", + "nativeName" => "Lingála" + ), + "lo" => array( + "name" => "Lao", + "nativeName" => "ພາສາລາວ" + ), + "lt" => array( + "name" => "Lithuanian", + "nativeName" => "lietuvių kalba" + ), + "lu" => array( + "name" => "Luba-Katanga", + "nativeName" => "" + ), + "lv" => array( + "name" => "Latvian", + "nativeName" => "latvieÅ¡u valoda" + ), + "gv" => array( + "name" => "Manx", + "nativeName" => "Gaelg, Gailck" + ), + "mk" => array( + "name" => "Macedonian", + "nativeName" => "македонÑки јазик" + ), + "mg" => array( + "name" => "Malagasy", + "nativeName" => "Malagasy fiteny" + ), + "ms" => array( + "name" => "Malay", + "nativeName" => "bahasa Melayu, بهاس ملايو‎" + ), + "ml" => array( + "name" => "Malayalam", + "nativeName" => "മലയാളം" + ), + "mt" => array( + "name" => "Maltese", + "nativeName" => "Malti" + ), + "mi" => array( + "name" => "MÄori", + "nativeName" => "te reo MÄori" + ), + "mr" => array( + "name" => "Marathi (MarÄá¹hÄ«)", + "nativeName" => "मराठी" + ), + "mh" => array( + "name" => "Marshallese", + "nativeName" => "Kajin M̧ajeļ" + ), + "mn" => array( + "name" => "Mongolian", + "nativeName" => "монгол" + ), + "na" => array( + "name" => "Nauru", + "nativeName" => "EkakairÅ© Naoero" + ), + "nv" => array( + "name" => "Navajo, Navaho", + "nativeName" => "Diné bizaad, DinékʼehǰÃ" + ), + "nb" => array( + "name" => "Norwegian BokmÃ¥l", + "nativeName" => "Norsk bokmÃ¥l" + ), + "nd" => array( + "name" => "North Ndebele", + "nativeName" => "isiNdebele" + ), + "ne" => array( + "name" => "Nepali", + "nativeName" => "नेपाली" + ), + "ng" => array( + "name" => "Ndonga", + "nativeName" => "Owambo" + ), + "nn" => array( + "name" => "Norwegian Nynorsk", + "nativeName" => "Norsk nynorsk" + ), + "no" => array( + "name" => "Norwegian", + "nativeName" => "Norsk" + ), + "ii" => array( + "name" => "Nuosu", + "nativeName" => "ê†ˆêŒ ê’¿ Nuosuhxop" + ), + "nr" => array( + "name" => "South Ndebele", + "nativeName" => "isiNdebele" + ), + "oc" => array( + "name" => "Occitan", + "nativeName" => "Occitan" + ), + "oj" => array( + "name" => "Ojibwe, Ojibwa", + "nativeName" => "áŠá“‚ᔑᓈá¯á’§áŽá“" + ), + "cu" => array( + "name" => "Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic", + "nativeName" => "ѩзыкъ ÑловѣньÑкъ" + ), + "om" => array( + "name" => "Oromo", + "nativeName" => "Afaan Oromoo" + ), + "or" => array( + "name" => "Oriya", + "nativeName" => "ଓଡ଼ିଆ" + ), + "os" => array( + "name" => "Ossetian, Ossetic", + "nativeName" => "ирон æвзаг" + ), + "pa" => array( + "name" => "Panjabi, Punjabi", + "nativeName" => "ਪੰਜਾਬੀ, پنجابی‎" + ), + "pi" => array( + "name" => "PÄli", + "nativeName" => "पाऴि" + ), + "fa" => array( + "name" => "Persian", + "nativeName" => "ÙØ§Ø±Ø³ÛŒ" + ), + "pl" => array( + "name" => "Polish", + "nativeName" => "polski" + ), + "ps" => array( + "name" => "Pashto, Pushto", + "nativeName" => "پښتو" + ), + "pt" => array( + "name" => "Portuguese", + "nativeName" => "Português" + ), + "qu" => array( + "name" => "Quechua", + "nativeName" => "Runa Simi, Kichwa" + ), + "rm" => array( + "name" => "Romansh", + "nativeName" => "rumantsch grischun" + ), + "rn" => array( + "name" => "Kirundi", + "nativeName" => "kiRundi" + ), + "ro" => array( + "name" => "Romanian, Moldavian, Moldovan", + "nativeName" => "română" + ), + "ru" => array( + "name" => "Russian", + "nativeName" => "руÑÑкий Ñзык" + ), + "sa" => array( + "name" => "Sanskrit (Saá¹ská¹›ta)", + "nativeName" => "संसà¥à¤•ृतमà¥" + ), + "sc" => array( + "name" => "Sardinian", + "nativeName" => "sardu" + ), + "sd" => array( + "name" => "Sindhi", + "nativeName" => "सिनà¥à¤§à¥€, سنڌي، سندھی‎" + ), + "se" => array( + "name" => "Northern Sami", + "nativeName" => "Davvisámegiella" + ), + "sm" => array( + "name" => "Samoan", + "nativeName" => "gagana faa Samoa" + ), + "sg" => array( + "name" => "Sango", + "nativeName" => "yângâ tî sängö" + ), + "sr" => array( + "name" => "Serbian", + "nativeName" => "ÑрпÑки језик" + ), + "gd" => array( + "name" => "Scottish Gaelic; Gaelic", + "nativeName" => "Gà idhlig" + ), + "sn" => array( + "name" => "Shona", + "nativeName" => "chiShona" + ), + "si" => array( + "name" => "Sinhala, Sinhalese", + "nativeName" => "සිංහල" + ), + "sk" => array( + "name" => "Slovak", + "nativeName" => "slovenÄina" + ), + "sl" => array( + "name" => "Slovene", + "nativeName" => "slovenÅ¡Äina" + ), + "so" => array( + "name" => "Somali", + "nativeName" => "Soomaaliga, af Soomaali" + ), + "st" => array( + "name" => "Southern Sotho", + "nativeName" => "Sesotho" + ), + "es" => array( + "name" => "Spanish; Castilian", + "nativeName" => "español, castellano" + ), + "su" => array( + "name" => "Sundanese", + "nativeName" => "Basa Sunda" + ), + "sw" => array( + "name" => "Swahili", + "nativeName" => "Kiswahili" + ), + "ss" => array( + "name" => "Swati", + "nativeName" => "SiSwati" + ), + "sv" => array( + "name" => "Swedish", + "nativeName" => "svenska" + ), + "ta" => array( + "name" => "Tamil", + "nativeName" => "தமிழà¯" + ), + "te" => array( + "name" => "Telugu", + "nativeName" => "తెలà±à°—à±" + ), + "tg" => array( + "name" => "Tajik", + "nativeName" => "тоҷикӣ, toÄŸikÄ«, تاجیکی‎" + ), + "th" => array( + "name" => "Thai", + "nativeName" => "ไทย" + ), + "ti" => array( + "name" => "Tigrinya", + "nativeName" => "ትáŒáˆáŠ›" + ), + "bo" => array( + "name" => "Tibetan Standard, Tibetan, Central", + "nativeName" => "བོད་ཡིག" + ), + "tk" => array( + "name" => "Turkmen", + "nativeName" => "Türkmen, Түркмен" + ), + "tl" => array( + "name" => "Tagalog", + "nativeName" => "Wikang Tagalog, áœáœ’ᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔" + ), + "tn" => array( + "name" => "Tswana", + "nativeName" => "Setswana" + ), + "to" => array( + "name" => "Tonga (Tonga Islands)", + "nativeName" => "faka Tonga" + ), + "tr" => array( + "name" => "Turkish", + "nativeName" => "Türkçe" + ), + "ts" => array( + "name" => "Tsonga", + "nativeName" => "Xitsonga" + ), + "tt" => array( + "name" => "Tatar", + "nativeName" => "татарча, tatarça, تاتارچا‎" + ), + "tw" => array( + "name" => "Twi", + "nativeName" => "Twi" + ), + "ty" => array( + "name" => "Tahitian", + "nativeName" => "Reo Tahiti" + ), + "ug" => array( + "name" => "Uighur, Uyghur", + "nativeName" => "UyÆ£urqÉ™, ئۇيغۇرچە‎" + ), + "uk" => array( + "name" => "Ukrainian", + "nativeName" => "українÑька" + ), + "ur" => array( + "name" => "Urdu", + "nativeName" => "اردو" + ), + "uz" => array( + "name" => "Uzbek", + "nativeName" => "zbek, Ўзбек, أۇزبÛك‎" + ), + "ve" => array( + "name" => "Venda", + "nativeName" => "Tshivenḓa" + ), + "vi" => array( + "name" => "Vietnamese", + "nativeName" => "Tiếng Việt" + ), + "vo" => array( + "name" => "Volapük", + "nativeName" => "Volapük" + ), + "wa" => array( + "name" => "Walloon", + "nativeName" => "Walon" + ), + "cy" => array( + "name" => "Welsh", + "nativeName" => "Cymraeg" + ), + "wo" => array( + "name" => "Wolof", + "nativeName" => "Wollof" + ), + "fy" => array( + "name" => "Western Frisian", + "nativeName" => "Frysk" + ), + "xh" => array( + "name" => "Xhosa", + "nativeName" => "isiXhosa" + ), + "yi" => array( + "name" => "Yiddish", + "nativeName" => "ייִדיש" + ), + "yo" => array( + "name" => "Yoruba", + "nativeName" => "Yorùbá" + ), + "za" => array( + "name" => "Zhuang, Chuang", + "nativeName" => "Saɯ cueŋƅ, Saw cuengh" + ) +); diff --git a/include/mysqli.php b/include/mysqli.php index 443a1544e9ff0b5d90b12009b2d6bb82dde71e8e..8955ea99f335685bba44f0b3570c7715ab39ee49 100644 --- a/include/mysqli.php +++ b/include/mysqli.php @@ -127,7 +127,22 @@ function db_create_database($database, $charset='utf8', $database, $charset, $collate)); } -// execute sql query +/** + * Function: db_query + * Execute sql query + * + * Parameters: + * $query - (string) SQL query (with parameters) + * $logError - (mixed): + * - (bool) true or false if error should be logged and alert email sent + * - (callable) to receive error number and return true or false if + * error should be logged and alert email sent. The callable is only + * invoked if the query fails. + * + * Returns: + * (mixed) MysqliResource if SELECT query succeeds, true if an INSERT, + * UPDATE, or DELETE succeeds, false or null if the query fails. + */ function db_query($query, $logError=true) { global $ost, $__db; @@ -140,6 +155,10 @@ function db_query($query, $logError=true) { } while (!$res && --$tries && $__db->errno == 1213); if(!$res && $logError && $ost) { //error reporting + // Allow $logError() callback to determine if logging is necessary + if (is_callable($logError) && !($logError($__db->errno))) + return $res; + $msg='['.$query.']'."\n\n".db_error(); $ost->logDBError('DB Error #'.db_errno(), $msg); //echo $msg; #uncomment during debuging or dev. diff --git a/include/pear/Net/DNS2.php b/include/pear/Net/DNS2.php new file mode 100644 index 0000000000000000000000000000000000000000..bffe0d45eb72d195e7bfc62504eb69bca1efd943 --- /dev/null +++ b/include/pear/Net/DNS2.php @@ -0,0 +1,1228 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DNS2.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/* + * register the auto-load function + * + */ +spl_autoload_register('Net_DNS2::autoload'); + +/** + * This is the base class for the Net_DNS2_Resolver and Net_DNS2_Updater + * classes. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Resolver, Net_DNS2_Updater + * + */ +class Net_DNS2 +{ + /* + * the current version of this library + */ + const VERSION = '1.3.1'; + + /* + * the default path to a resolv.conf file + */ + const RESOLV_CONF = '/etc/resolv.conf'; + + /* + * use TCP only (true/false) + */ + public $use_tcp = false; + + /* + * DNS Port to use (53) + */ + public $dns_port = 53; + + /* + * the ip/port for use as a local socket + */ + public $local_host = ''; + public $local_port = 0; + + /* + * timeout value for socket connections + */ + public $timeout = 5; + + /* + * randomize the name servers list + */ + public $ns_random = false; + + /* + * default domains + */ + public $domain = ''; + + /* + * domain search list - not actually used right now + */ + public $search_list = array(); + + /* + * enable cache; either "shared", "file" or "none" + */ + public $cache_type = 'none'; + + /* + * file name to use for shared memory segment or file cache + */ + public $cache_file = '/tmp/net_dns2.cache'; + + /* + * the max size of the cache file (in bytes) + */ + public $cache_size = 10000; + + /* + * the method to use for storing cache data; either "serialize" or "json" + * + * json is faster, but can't remember the class names (everything comes back + * as a "stdClass Object"; all the data is the same though. serialize is + * slower, but will have all the class info. + * + * defaults to 'serialize' + */ + public $cache_serializer = 'serialize'; + + /* + * by default, according to RFC 1034 + * + * CNAME RRs cause special action in DNS software. When a name server + * fails to find a desired RR in the resource set associated with the + * domain name, it checks to see if the resource set consists of a CNAME + * record with a matching class. If so, the name server includes the CNAME + * record in the response and restarts the query at the domain name + * specified in the data field of the CNAME record. + * + * this can cause "unexpected" behavious, since i'm sure *most* people + * don't know DNS does this; there may be cases where Net_DNS2 returns a + * positive response, even though the hostname the user looked up did not + * actually exist. + * + * strict_query_mode means that if the hostname that was looked up isn't + * actually in the answer section of the response, Net_DNS2 will return an + * empty answer section, instead of an answer section that could contain + * CNAME records. + * + */ + public $strict_query_mode = false; + + /* + * if we should set the recursion desired bit to 1 or 0. + * + * by default this is set to true, we want the DNS server to perform a recursive + * request. If set to false, the RD bit will be set to 0, and the server will + * not perform recursion on the request. + */ + public $recurse = true; + + /* + * request DNSSEC values, by setting the DO flag to 1; this actually makes + * the resolver add a OPT RR to the additional section, and sets the DO flag + * in this RR to 1 + * + */ + public $dnssec = false; + + /* + * set the DNSSEC AD (Authentic Data) bit on/off; the AD bit on the request + * side was previously undefined, and resolvers we instructed to always clear + * the AD bit when sending a request. + * + * RFC6840 section 5.7 defines setting the AD bit in the query as a signal to + * the server that it wants the value of the AD bit, without needed to request + * all the DNSSEC data via the DO bit. + * + */ + public $dnssec_ad_flag = false; + + /* + * set the DNSSEC CD (Checking Disabled) bit on/off; turning this off, means + * that the DNS resolver will perform it's own signature validation- so the DNS + * servers simply pass through all the details. + * + */ + public $dnssec_cd_flag = false; + + /* + * the EDNS(0) UDP payload size to use when making DNSSEC requests + * see RFC 2671 section 6.2.3 for more details + * + * http://tools.ietf.org/html/draft-ietf-dnsext-rfc2671bis-edns0-10 + * + */ + public $dnssec_payload_size = 1280; + + /* + * local sockets + */ + protected $sock = array('udp' => array(), 'tcp' => array()); + + /* + * name server list + */ + protected $nameservers = array(); + + /* + * if the socket extension is loaded + */ + protected $sockets_enabled = false; + + /* + * the TSIG or SIG RR object for authentication + */ + protected $auth_signature = null; + + /* + * the shared memory segment id for the local cache + */ + protected $cache = null; + + /* + * internal setting for enabling cache + */ + protected $use_cache = false; + + /* + * the last erro message returned by the sockets class + */ + private $_last_socket_error = ''; + + /** + * Constructor - base constructor for the Resolver and Updater + * + * @param mixed $options array of options or null for none + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct(array $options = null) + { + // + // check for the sockets extension + // + $this->sockets_enabled = extension_loaded('sockets'); + + // + // load any options that were provided + // + if (!empty($options)) { + + foreach ($options as $key => $value) { + + if ($key == 'nameservers') { + + $this->setServers($value); + } else { + + $this->$key = $value; + } + } + } + + // + // if we're set to use the local shared memory cache, then + // make sure it's been initialized + // + switch($this->cache_type) { + case 'shared': + if (extension_loaded('shmop')) { + + $this->cache = new Net_DNS2_Cache_Shm; + $this->use_cache = true; + } else { + + throw new Net_DNS2_Exception( + 'shmop library is not available for cache', + Net_DNS2_Lookups::E_CACHE_SHM_UNAVAIL + ); + } + break; + case 'file': + + $this->cache = new Net_DNS2_Cache_File; + $this->use_cache = true; + + break; + case 'none': + $this->use_cache = false; + break; + default: + + throw new Net_DNS2_Exception( + 'un-supported cache type: ' . $this->cache_type, + Net_DNS2_Lookups::E_CACHE_UNSUPPORTED + ); + } + } + + /** + * autoload call-back function; used to auto-load classes + * + * @param string $name the name of the class + * + * @return void + * @access public + * + */ + static public function autoload($name) + { + // + // only auto-load our classes + // + if (strncmp($name, 'Net_DNS2', 8) == 0) { + + include str_replace('_', '/', $name) . '.php'; + } + + return; + } + + /** + * sets the name servers to be used + * + * @param mixed $nameservers either an array of name servers, or a file name + * to parse, assuming it's in the resolv.conf format + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function setServers($nameservers) + { + // + // if it's an array, then use it directly + // + // otherwise, see if it's a path to a resolv.conf file and if so, load it + // + if (is_array($nameservers)) { + + $this->nameservers = $nameservers; + + } else { + + // + // check to see if the file is readable + // + if (is_readable($nameservers) === true) { + + $data = file_get_contents($nameservers); + if ($data === false) { + throw new Net_DNS2_Exception( + 'failed to read contents of file: ' . $nameservers, + Net_DNS2_Lookups::E_NS_INVALID_FILE + ); + } + + $lines = explode("\n", $data); + + foreach ($lines as $line) { + + $line = trim($line); + + // + // ignore empty lines, and lines that are commented out + // + if ( (strlen($line) == 0) + || ($line[0] == '#') + || ($line[0] == ';') + ) { + continue; + } + + list($key, $value) = preg_split('/\s+/', $line, 2); + + $key = trim(strtolower($key)); + $value = trim(strtolower($value)); + + switch($key) { + case 'nameserver': + + // + // nameserver can be a IPv4 or IPv6 address + // + if ( (self::isIPv4($value) == true) + || (self::isIPv6($value) == true) + ) { + + $this->nameservers[] = $value; + } else { + + throw new Net_DNS2_Exception( + 'invalid nameserver entry: ' . $value, + Net_DNS2_Lookups::E_NS_INVALID_ENTRY + ); + } + break; + + case 'domain': + $this->domain = $value; + break; + + case 'search': + $this->search_list = preg_split('/\s+/', $value); + break; + + default: + ; + } + } + + // + // if we don't have a domain, but we have a search list, then + // take the first entry on the search list as the domain + // + if ( (strlen($this->domain) == 0) + && (count($this->search_list) > 0) + ) { + $this->domain = $this->search_list[0]; + } + + } else { + throw new Net_DNS2_Exception( + 'resolver file file provided is not readable: ' . $nameservers, + Net_DNS2_Lookups::E_NS_INVALID_FILE + ); + } + } + + // + // check the name servers + // + $this->checkServers(); + + return true; + } + + /** + * checks the list of name servers to make sure they're set + * + * @param mixed $default a path to a resolv.conf file or an array of servers. + * + * @return boolean + * @throws Net_DNS2_Exception + * @access protected + * + */ + protected function checkServers($default = null) + { + if (empty($this->nameservers)) { + + if (isset($default)) { + + $this->setServers($default); + } else { + + throw new Net_DNS2_Exception( + 'empty name servers list; you must provide a list of name '. + 'servers, or the path to a resolv.conf file.', + Net_DNS2_Lookups::E_NS_INVALID_ENTRY + ); + } + } + + return true; + } + + /** + * adds a TSIG RR object for authentication + * + * @param string $keyname the key name to use for the TSIG RR + * @param string $signature the key to sign the request. + * @param string $algorithm the algorithm to use + * + * @return boolean + * @access public + * @since function available since release 1.1.0 + * + */ + public function signTSIG( + $keyname, $signature = '', $algorithm = Net_DNS2_RR_TSIG::HMAC_MD5 + ) { + // + // if the TSIG was pre-created and passed in, then we can just used + // it as provided. + // + if ($keyname instanceof Net_DNS2_RR_TSIG) { + + $this->auth_signature = $keyname; + + } else { + + // + // otherwise create the TSIG RR, but don't add it just yet; TSIG needs + // to be added as the last additional entry- so we'll add it just + // before we send. + // + $this->auth_signature = Net_DNS2_RR::fromString( + strtolower(trim($keyname)) . + ' TSIG '. $signature + ); + + // + // set the algorithm to use + // + $this->auth_signature->algorithm = $algorithm; + } + + return true; + } + + /** + * adds a SIG RR object for authentication + * + * @param string $filename the name of a file to load the signature from. + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * @since function available since release 1.1.0 + * + */ + public function signSIG0($filename) + { + // + // check for OpenSSL + // + if (extension_loaded('openssl') === false) { + + throw new Net_DNS2_Exception( + 'the OpenSSL extension is required to use SIG(0).', + Net_DNS2_Lookups::E_OPENSSL_UNAVAIL + ); + } + + // + // if the SIG was pre-created, then use it as-is + // + if ($filename instanceof Net_DNS2_RR_SIG) { + + $this->auth_signature = $filename; + + } else { + + // + // otherwise, it's filename which needs to be parsed and processed. + // + $private = new Net_DNS2_PrivateKey($filename); + + // + // create a new Net_DNS2_RR_SIG object + // + $this->auth_signature = new Net_DNS2_RR_SIG(); + + // + // reset some values + // + $this->auth_signature->name = $private->signname; + $this->auth_signature->ttl = 0; + $this->auth_signature->class = 'ANY'; + + // + // these values are pulled from the private key + // + $this->auth_signature->algorithm = $private->algorithm; + $this->auth_signature->keytag = $private->keytag; + $this->auth_signature->signname = $private->signname; + + // + // these values are hard-coded for SIG0 + // + $this->auth_signature->typecovered = 'SIG0'; + $this->auth_signature->labels = 0; + $this->auth_signature->origttl = 0; + + // + // generate the dates + // + $t = time(); + + $this->auth_signature->sigincep = gmdate('YmdHis', $t); + $this->auth_signature->sigexp = gmdate('YmdHis', $t + 500); + + // + // store the private key in the SIG object for later. + // + $this->auth_signature->private_key = $private; + } + + // + // only RSAMD5 and RSASHA1 are supported for SIG(0) + // + switch($this->auth_signature->algorithm) { + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: + break; + default: + throw new Net_DNS2_Exception( + 'only asymmetric algorithms work with SIG(0)!', + Net_DNS2_Lookups::E_OPENSSL_INV_ALGO + ); + } + + return true; + } + + /** + * a simple function to determine if the RR type is cacheable + * + * @param stream $_type the RR type string + * + * @return bool returns true/false if the RR type if cachable + * @access public + * + */ + public function cacheable($_type) + { + switch($_type) { + case 'AXFR': + case 'OPT': + return false; + } + + return true; + } + + /** + * PHP doesn't support unsigned integers, but many of the RR's return + * unsigned values (like SOA), so there is the possibility that the + * value will overrun on 32bit systems, and you'll end up with a + * negative value. + * + * 64bit systems are not affected, as their PHP_IN_MAX value should + * be 64bit (ie 9223372036854775807) + * + * This function returns a negative integer value, as a string, with + * the correct unsigned value. + * + * @param string $_int the unsigned integer value to check + * + * @return string returns the unsigned value as a string. + * @access public + * + */ + public static function expandUint32($_int) + { + if ( ($_int < 0) && (PHP_INT_MAX == 2147483647) ) { + return sprintf('%u', $_int); + } else { + return $_int; + } + } + + /** + * returns true/false if the given address is a valid IPv4 address + * + * @param string $_address the IPv4 address to check + * + * @return boolean returns true/false if the address is IPv4 address + * @access public + * + */ + public static function isIPv4($_address) + { + // + // use filter_var() if it's available; it's faster than preg + // + if (extension_loaded('filter') == true) { + + if (filter_var( + $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 + ) == false) { + return false; + } + } else { + + // + // do the main check here; + // + if (inet_pton($_address) === false) { + return false; + } + + // + // then make sure we're not a IPv6 address + // + if (preg_match( + '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', + $_address + ) == 0) { + return false; + } + } + + return true; + } + + /** + * returns true/false if the given address is a valid IPv6 address + * + * @param string $_address the IPv6 address to check + * + * @return boolean returns true/false if the address is IPv6 address + * @access public + * + */ + public static function isIPv6($_address) + { + // + // use filter_var() if it's available; it's faster than preg + // + if (extension_loaded('filter') == true) { + if (filter_var( + $_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 + ) == false) { + return false; + } + } else { + + // + // do the main check here + // + if (inet_pton($_address) === false) { + return false; + } + + // + // then make sure it doesn't match a IPv4 address + // + if (preg_match( + '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_address + ) == 1) { + return false; + } + } + + return true; + } + + /** + * formats the given IPv6 address as a fully expanded IPv6 address + * + * @param string $_address the IPv6 address to expand + * + * @return string the fully expanded IPv6 address + * @access public + * + */ + public static function expandIPv6($_address) + { + if (strpos($_address, '::') !== false) { + + $part = explode('::', $_address); + $part[0] = explode(':', $part[0]); + $part[1] = explode(':', $part[1]); + + $missing = array(); + + $x = (8 - (count($part[0]) + count($part[1]))); + for ($i = 0; $i < $x; $i++) { + + array_push($missing, '0000'); + } + + $missing = array_merge($part[0], $missing); + $part = array_merge($missing, $part[1]); + + } else { + + $part = explode(':', $_address); + } + + foreach ($part as &$p) { + while (strlen($p) < 4) { + $p = '0' . $p; + } + } + + unset($p); + + $result = implode(':', $part); + + if (strlen($result) == 39) { + return $result; + } else { + return false; + } + } + + /** + * sends a standard Net_DNS2_Packet_Request packet + * + * @param Net_DNS2_Packet $request a Net_DNS2_Packet_Request object + * @param boolean $use_tcp true/false if the function should + * use TCP for the request + * + * @return mixed returns a Net_DNS2_Packet_Response object, or false on error + * @throws Net_DNS2_Exception + * @access protected + * + */ + protected function sendPacket(Net_DNS2_Packet $request, $use_tcp) + { + // + // get the data from the packet + // + $data = $request->get(); + if (strlen($data) < Net_DNS2_Lookups::DNS_HEADER_SIZE) { + + throw new Net_DNS2_Exception( + 'invalid or empty packet for sending!', + Net_DNS2_Lookups::E_PACKET_INVALID, + null, + $request + ); + } + + reset($this->nameservers); + + // + // randomize the name server list if it's asked for + // + if ($this->ns_random == true) { + + shuffle($this->nameservers); + } + + // + // loop so we can handle server errors + // + $response = null; + $ns = ''; + $socket_type = null; + $tcp_fallback = false; + + while (1) { + + // + // grab the next DNS server + // + if ($tcp_fallback == false) { + + $ns = each($this->nameservers); + if ($ns === false) { + + throw new Net_DNS2_Exception( + 'every name server provided has failed: ' . + $this->_last_socket_error, + Net_DNS2_Lookups::E_NS_FAILED + ); + } + + $ns = $ns[1]; + } + + // + // if the use TCP flag (force TCP) is set, or the packet is bigger + // than 512 bytes, use TCP for sending the packet + // + if ( ($use_tcp == true) + || (strlen($data) > Net_DNS2_Lookups::DNS_MAX_UDP_SIZE) + || ($tcp_fallback == true) + ) { + $tcp_fallback = false; + $socket_type = Net_DNS2_Socket::SOCK_STREAM; + + // + // create the socket object + // + if ( (!isset($this->sock['tcp'][$ns])) + || (!($this->sock['tcp'][$ns] instanceof Net_DNS2_Socket)) + ) { + if ($this->sockets_enabled === true) { + + $this->sock['tcp'][$ns] = new Net_DNS2_Socket_Sockets( + Net_DNS2_Socket::SOCK_STREAM, + $ns, + $this->dns_port, + $this->timeout + ); + } else { + + $this->sock['tcp'][$ns] = new Net_DNS2_Socket_Streams( + Net_DNS2_Socket::SOCK_STREAM, + $ns, + $this->dns_port, + $this->timeout + ); + } + } + + // + // if a local IP address / port is set, then add it + // + if (strlen($this->local_host) > 0) { + + $this->sock['tcp'][$ns]->bindAddress( + $this->local_host, $this->local_port + ); + } + + // + // open it; if it fails, continue in the while loop + // + if ($this->sock['tcp'][$ns]->open() === false) { + + $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; + continue; + } + + // + // write the data to the socket; if it fails, continue on + // the while loop + // + if ($this->sock['tcp'][$ns]->write($data) === false) { + + $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; + continue; + } + + // + // read the content, using select to wait for a response + // + $size = 0; + $result = null; + + // + // handle zone transfer requests differently than other requests. + // + if ($request->question[0]->qtype == 'AXFR') { + + $soa_count = 0; + + while (1) { + + // + // read the data off the socket + // + $result = $this->sock['tcp'][$ns]->read($size); + if ( ($result === false) + || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE) + ) { + $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; + break; + } + + // + // parse the first chunk as a packet + // + $chunk = new Net_DNS2_Packet_Response($result, $size); + + // + // if this is the first packet, then clone it directly, then + // go through it to see if there are two SOA records + // (indicating that it's the only packet) + // + if (is_null($response) == true) { + + $response = clone $chunk; + + // + // look for a failed response; if the zone transfer + // failed, then we don't need to do anything else at this + // point, and we should just break out. + // + if ($response->header->rcode != Net_DNS2_Lookups::RCODE_NOERROR) { + break; + } + + // + // go through each answer + // + foreach ($response->answer as $index => $rr) { + + // + // count the SOA records + // + if ($rr->type == 'SOA') { + $soa_count++; + } + } + + // + // if we have 2 or more SOA records, then we're done; + // otherwise continue out so we read the rest of the + // packets off the socket + // + if ($soa_count >= 2) { + break; + } else { + continue; + } + + } else { + + // + // go through all these answers, and look for SOA records + // + foreach ($chunk->answer as $index => $rr) { + + // + // count the number of SOA records we find + // + if ($rr->type == 'SOA') { + $soa_count++; + } + + // + // add the records to a single response object + // + $response->answer[] = $rr; + } + + // + // if we've found the second SOA record, we're done + // + if ($soa_count >= 2) { + break; + } + } + } + + } else { + + $result = $this->sock['tcp'][$ns]->read($size); + if ( ($result === false) + || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE) + ) { + $this->_last_socket_error = $this->sock['tcp'][$ns]->last_error; + continue; + } + + // + // create the packet object + // + $response = new Net_DNS2_Packet_Response($result, $size); + } + + break; + + } else { + + $socket_type = Net_DNS2_Socket::SOCK_DGRAM; + + // + // create the socket object + // + if ( (!isset($this->sock['udp'][$ns])) + || (!($this->sock['udp'][$ns] instanceof Net_DNS2_Socket)) + ) { + if ($this->sockets_enabled === true) { + + $this->sock['udp'][$ns] = new Net_DNS2_Socket_Sockets( + Net_DNS2_Socket::SOCK_DGRAM, $ns, $this->dns_port, $this->timeout + ); + } else { + + $this->sock['udp'][$ns] = new Net_DNS2_Socket_Streams( + Net_DNS2_Socket::SOCK_DGRAM, $ns, $this->dns_port, $this->timeout + ); + } + } + + // + // if a local IP address / port is set, then add it + // + if (strlen($this->local_host) > 0) { + + $this->sock['udp'][$ns]->bindAddress( + $this->local_host, $this->local_port + ); + } + + // + // open it + // + if ($this->sock['udp'][$ns]->open() === false) { + + $this->_last_socket_error = $this->sock['udp'][$ns]->last_error; + continue; + } + + // + // write the data to the socket + // + if ($this->sock['udp'][$ns]->write($data) === false) { + + $this->_last_socket_error = $this->sock['udp'][$ns]->last_error; + continue; + } + + // + // read the content, using select to wait for a response + // + $size = 0; + + $result = $this->sock['udp'][$ns]->read($size); + if (( $result === false) + || ($size < Net_DNS2_Lookups::DNS_HEADER_SIZE) + ) { + $this->_last_socket_error = $this->sock['udp'][$ns]->last_error; + continue; + } + + // + // create the packet object + // + $response = new Net_DNS2_Packet_Response($result, $size); + if (is_null($response)) { + + throw new Net_DNS2_Exception( + 'empty response object', + Net_DNS2_Lookups::E_NS_FAILED, + null, + $request + ); + } + + // + // check the packet header for a trucated bit; if it was truncated, + // then re-send the request as TCP. + // + if ($response->header->tc == 1) { + + $tcp_fallback = true; + continue; + } + + break; + } + } + + // + // if $response is null, then we didn't even try once; which shouldn't + // actually ever happen + // + if (is_null($response)) { + + throw new Net_DNS2_Exception( + 'empty response object', + Net_DNS2_Lookups::E_NS_FAILED, + null, + $request + ); + } + + // + // add the name server that the response came from to the response object, + // and the socket type that was used. + // + $response->answer_from = $ns; + $response->answer_socket_type = $socket_type; + + // + // make sure header id's match between the request and response + // + if ($request->header->id != $response->header->id) { + + throw new Net_DNS2_Exception( + 'invalid header: the request and response id do not match.', + Net_DNS2_Lookups::E_HEADER_INVALID, + null, + $request, + $response + ); + } + + // + // make sure the response is actually a response + // + // 0 = query, 1 = response + // + if ($response->header->qr != Net_DNS2_Lookups::QR_RESPONSE) { + + throw new Net_DNS2_Exception( + 'invalid header: the response provided is not a response packet.', + Net_DNS2_Lookups::E_HEADER_INVALID, + null, + $request, + $response + ); + } + + // + // make sure the response code in the header is ok + // + if ($response->header->rcode != Net_DNS2_Lookups::RCODE_NOERROR) { + + throw new Net_DNS2_Exception( + 'DNS request failed: ' . + Net_DNS2_Lookups::$result_code_messages[$response->header->rcode], + $response->header->rcode, + null, + $request, + $response + ); + } + + return $response; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/BitMap.php b/include/pear/Net/DNS2/BitMap.php new file mode 100644 index 0000000000000000000000000000000000000000..cdc0934a3432e9f7de869a0f59d1013cdd5c89bc --- /dev/null +++ b/include/pear/Net/DNS2/BitMap.php @@ -0,0 +1,254 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: BitMap.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * a class to handle converting RR bitmaps to arrays and back; used on NSEC + * and NSEC3 RR's + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_BitMap +{ + /** + * parses a RR bitmap field defined in RFC3845, into an array of RR names. + * + * Type Bit Map(s) Field = ( Window Block # | Bitmap Length | Bitmap ) + + * + * @param string $data a bitmap stringto parse + * + * @return array + * @access public + * + */ + public static function bitMapToArray($data) + { + if (strlen($data) == 0) { + return null; + } + + $output = array(); + $offset = 0; + $length = strlen($data); + + while ($offset < $length) { + + // + // unpack the window and length values + // + $x = unpack('@' . $offset . '/Cwindow/Clength', $data); + $offset += 2; + + // + // copy out the bitmap value + // + $bitmap = unpack('C*', substr($data, $offset, $x['length'])); + $offset += $x['length']; + + // + // I'm not sure if there's a better way of doing this, but PHP doesn't + // have a 'B' flag for unpack() + // + $bitstr = ''; + foreach ($bitmap as $r) { + + $bitstr .= sprintf('%08b', $r); + } + + $blen = strlen($bitstr); + for ($i=0; $i<$blen; $i++) { + + if ($bitstr[$i] == '1') { + + $type = $x['window'] * 256 + $i; + + if (isset(Net_DNS2_Lookups::$rr_types_by_id[$type])) { + + $output[] = Net_DNS2_Lookups::$rr_types_by_id[$type]; + } else { + + $output[] = 'TYPE' . $type; + } + } + } + } + + return $output; + } + + /** + * builds a RR Bit map from an array of RR type names + * + * @param array $data a list of RR names + * + * @return string + * @access public + * + */ + public static function arrayToBitMap(array $data) + { + if (count($data) == 0) { + return null; + } + + $current_window = 0; + + // + // go through each RR + // + $max = 0; + $bm = array(); + + foreach ($data as $rr) { + + $rr = strtoupper($rr); + + // + // get the type id for the RR + // + $type = @Net_DNS2_Lookups::$rr_types_by_name[$rr]; + if (isset($type)) { + + // + // skip meta types or qtypes + // + if ( (isset(Net_DNS2_Lookups::$rr_qtypes_by_id[$type])) + || (isset(Net_DNS2_Lookups::$rr_metatypes_by_id[$type])) + ) { + continue; + } + + } else { + + // + // if it's not found, then it must be defined as TYPE<id>, per + // RFC3845 section 2.2, if it's not, we ignore it. + // + list($name, $type) = explode('TYPE', $rr); + if (!isset($type)) { + + continue; + } + } + + // + // build the current window + // + $current_window = (int)($type / 256); + + $val = $type - $current_window * 256.0; + if ($val > $max) { + $max = $val; + } + + $bm[$current_window][$val] = 1; + $bm[$current_window]['length'] = ceil(($max + 1) / 8); + } + + $output = ''; + + foreach ($bm as $window => $bitdata) { + + $bitstr = ''; + + for ($i=0; $i<$bm[$window]['length'] * 8; $i++) { + if (isset($bm[$window][$i])) { + $bitstr .= '1'; + } else { + $bitstr .= '0'; + } + } + + $output .= pack('CC', $window, $bm[$window]['length']); + $output .= pack('H*', self::bigBaseConvert($bitstr)); + } + + return $output; + } + + /** + * a base_convert that handles large numbers; forced to 2/16 + * + * @param string $number a bit string + * + * @return string + * @access public + * + */ + public static function bigBaseConvert($number) + { + $result = ''; + + $bin = substr(chunk_split(strrev($number), 4, '-'), 0, -1); + $temp = preg_split('[-]', $bin, -1, PREG_SPLIT_DELIM_CAPTURE); + + for ($i = count($temp)-1;$i >= 0;$i--) { + + $result = $result . base_convert(strrev($temp[$i]), 2, 16); + } + + return strtoupper($result); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Cache.php b/include/pear/Net/DNS2/Cache.php new file mode 100644 index 0000000000000000000000000000000000000000..b375ca2fa5bdc395834ae5ddc6999eeea626a356 --- /dev/null +++ b/include/pear/Net/DNS2/Cache.php @@ -0,0 +1,290 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Cache.php 160 2012-07-18 03:57:32Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.1.0 + * + */ + +/** + * A class to provide simple dns lookup caching. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Cache +{ + /* + * the filename of the cache file + */ + protected $cache_file = ''; + + /* + * the local data store for the cache + */ + protected $cache_data = array(); + + /* + * the size of the cache to use + */ + protected $cache_size = 0; + + /* + * the cache serializer + */ + protected $cache_serializer; + + /** + * returns true/false if the provided key is defined in the cache + * + * @param string $key the key to lookup in the local cache + * + * @return boolean + * @access public + * + */ + public function has($key) + { + return isset($this->cache_data[$key]); + } + + /** + * returns the value for the given key + * + * @param string $key the key to lookup in the local cache + * + * @return mixed returns the cache data on sucess, false on error + * @access public + * + */ + public function get($key) + { + if (isset($this->cache_data[$key])) { + + if ($this->cache_serializer == 'json') { + return json_decode($this->cache_data[$key]['object']); + } else { + return unserialize($this->cache_data[$key]['object']); + } + } else { + + return false; + } + } + + /** + * adds a new key/value pair to the cache + * + * @param string $key the key for the new cache entry + * @param mixed $data the data to store in cache + * + * @return void + * @access public + * + */ + public function put($key, $data) + { + $ttl = 86400 * 365; + + // + // find the lowest TTL, and use that as the TTL for the whole cached + // object. The downside to using one TTL for the whole object, is that + // we'll invalidate entries before they actuall expire, causing a + // real lookup to happen. + // + // The upside is that we don't need to require() each RR type in the + // cache, so we can look at their individual TTL's on each run- we only + // unserialize the actual RR object when it's get() from the cache. + // + foreach ($data->answer as $index => $rr) { + + if ($rr->ttl < $ttl) { + $ttl = $rr->ttl; + } + } + foreach ($data->authority as $index => $rr) { + + if ($rr->ttl < $ttl) { + $ttl = $rr->ttl; + } + } + foreach ($data->additional as $index => $rr) { + + if ($rr->ttl < $ttl) { + $ttl = $rr->ttl; + } + } + + $this->cache_data[$key] = array( + + 'cache_date' => time(), + 'ttl' => $ttl + ); + + if ($this->cache_serializer == 'json') { + $this->cache_data[$key]['object'] = json_encode($data); + } else { + $this->cache_data[$key]['object'] = serialize($data); + } + } + + /** + * runs a clean up process on the cache data + * + * @return void + * @access protected + * + */ + protected function clean() + { + if (count($this->cache_data) > 0) { + + // + // go through each entry and adjust their TTL, and remove entries that + // have expired + // + $now = time(); + + foreach ($this->cache_data as $key => $data) { + + $diff = $now - $data['cache_date']; + + if ($data['ttl'] <= $diff) { + + unset($this->cache_data[$key]); + } else { + + $this->cache_data[$key]['ttl'] -= $diff; + $this->cache_data[$key]['cache_date'] = $now; + } + } + } + } + + /** + * runs a clean up process on the cache data + * + * @return mixed + * @access protected + * + */ + protected function resize() + { + if (count($this->cache_data) > 0) { + + // + // serialize the cache data + // + if ($this->cache_serializer == 'json') { + $cache = json_encode($this->cache_data); + } else { + $cache = serialize($this->cache_data); + } + + // + // only do this part if the size allocated to the cache storage + // is smaller than the actual cache data + // + if (strlen($cache) > $this->cache_size) { + + while (strlen($cache) > $this->cache_size) { + + // + // go through the data, and remove the entries closed to + // their expiration date. + // + $smallest_ttl = time(); + $smallest_key = null; + + foreach ($this->cache_data as $key => $data) { + + if ($data['ttl'] < $smallest_ttl) { + + $smallest_ttl = $data['ttl']; + $smallest_key = $key; + } + } + + // + // unset the key with the smallest TTL + // + unset($this->cache_data[$smallest_key]); + + // + // re-serialize + // + if ($this->cache_serializer == 'json') { + $cache = json_encode($this->cache_data); + } else { + $cache = serialize($this->cache_data); + } + } + } + + if ( ($cache == 'a:0:{}') || ($cache == '{}') ) { + return null; + } else { + return $cache; + } + } + + return null; + } +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Cache/File.php b/include/pear/Net/DNS2/Cache/File.php new file mode 100644 index 0000000000000000000000000000000000000000..0de797747b11841ccd490f106838b37e971bcb17 --- /dev/null +++ b/include/pear/Net/DNS2/Cache/File.php @@ -0,0 +1,242 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: File.php 160 2012-07-18 03:57:32Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.1.0 + * + */ + +/** + * File-based caching for the Net_DNS2_Cache class + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Cache_File extends Net_DNS2_Cache +{ + /** + * open a cache object + * + * @param string $cache_file path to a file to use for cache storage + * @param integer $size the size of the shared memory segment to create + * @param string $serializer the name of the cache serialize to use + * + * @throws Net_DNS2_Exception + * @access public + * @return void + * + */ + public function open($cache_file, $size, $serializer) + { + $this->cache_size = $size; + $this->cache_file = $cache_file; + $this->cache_serializer = $serializer; + + // + // check that the file exists first + // + if ( (file_exists($this->cache_file) == true) + && (filesize($this->cache_file) > 0) + ) { + + // + // open the file for reading + // + $fp = @fopen($this->cache_file, 'r'); + if ($fp !== false) { + + // + // lock the file just in case + // + flock($fp, LOCK_EX); + + // + // read the file contents + // + $data = fread($fp, filesize($this->cache_file)); + + $decoded = null; + + if ($this->cache_serializer == 'json') { + + $decoded = json_decode($data, true); + } else { + + $decoded = unserialize($data); + } + + if (is_array($decoded) == true) { + + $this->cache_data = $decoded; + } else { + + $this->cache_data = array(); + } + + // + // unlock + // + flock($fp, LOCK_UN); + + // + // close the file + // + fclose($fp); + + // + // clean up the data + // + $this->clean(); + } + } + } + + /** + * Destructor + * + * @access public + * + */ + public function __destruct() + { + // + // if there's no cache file set, then there's nothing to do + // + if (strlen($this->cache_file) == 0) { + return; + } + + // + // open the file for reading/writing + // + $fp = fopen($this->cache_file, 'a+'); + if ($fp !== false) { + + // + // lock the file just in case + // + flock($fp, LOCK_EX); + + // + // seek to the start of the file to read + // + fseek($fp, 0, SEEK_SET); + + // + // read the file contents + // + $data = @fread($fp, filesize($this->cache_file)); + if ( ($data !== false) && (strlen($data) > 0) ) { + + // + // unserialize and store the data + // + $c = $this->cache_data; + + $decoded = null; + + if ($this->cache_serializer == 'json') { + + $decoded = json_decode($data, true); + } else { + + $decoded = unserialize($data); + } + + if (is_array($decoded) == true) { + + $this->cache_data = array_merge($c, $decoded); + } + } + + // + // trucate the file + // + ftruncate($fp, 0); + + // + // clean the data + // + $this->clean(); + + // + // resize the data + // + $data = $this->resize(); + if (!is_null($data)) { + + // + // write the file contents + // + fwrite($fp, $data); + } + + // + // unlock + // + flock($fp, LOCK_UN); + + // + // close the file + // + fclose($fp); + } + } +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Cache/Shm.php b/include/pear/Net/DNS2/Cache/Shm.php new file mode 100644 index 0000000000000000000000000000000000000000..5737efe71708adce07ad0ec8076c7d9990c759e0 --- /dev/null +++ b/include/pear/Net/DNS2/Cache/Shm.php @@ -0,0 +1,305 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Shm.php 160 2012-07-18 03:57:32Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.1.0 + * + */ + +/** + * Shared Memory-based caching for the Net_DNS2_Cache class + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Cache_Shm extends Net_DNS2_Cache +{ + /* + * resource id of the shared memory cache + */ + private $_cache_id = false; + + /* + * the IPC key + */ + private $_cache_file_tok = -1; + + /** + * open a cache object + * + * @param string $cache_file path to a file to use for cache storage + * @param integer $size the size of the shared memory segment to create + * @param string $serializer the name of the cache serialize to use + * + * @throws Net_DNS2_Exception + * @access public + * @return void + * + */ + public function open($cache_file, $size, $serializer) + { + $this->cache_size = $size; + $this->cache_file = $cache_file; + $this->cache_serializer = $serializer; + + // + // make sure the file exists first + // + if (!file_exists($cache_file)) { + + if (file_put_contents($cache_file, '') === false) { + + throw new Net_DNS2_Exception( + 'failed to create empty SHM file: ' . $cache_file, + Net_DNS2_Lookups::E_CACHE_SHM_FILE + ); + } + } + + // + // convert the filename to a IPC key + // + $this->_cache_file_tok = ftok($cache_file, 't'); + if ($this->_cache_file_tok == -1) { + + throw new Net_DNS2_Exception( + 'failed on ftok() file: ' . $this->_cache_file_tok, + Net_DNS2_Lookups::E_CACHE_SHM_FILE + ); + } + + // + // try to open an existing cache; if it doesn't exist, then there's no + // cache, and nothing to do. + // + $this->_cache_id = @shmop_open($this->_cache_file_tok, 'w', 0, 0); + if ($this->_cache_id !== false) { + + // + // this returns the size allocated, and not the size used, but it's + // still a good check to make sure there's space allocated. + // + $allocated = shmop_size($this->_cache_id); + if ($allocated > 0) { + + // + // read the data from the shared memory segment + // + $data = trim(shmop_read($this->_cache_id, 0, $allocated)); + if ( ($data !== false) && (strlen($data) > 0) ) { + + // + // unserialize and store the data + // + $decoded = null; + + if ($this->cache_serializer == 'json') { + + $decoded = json_decode($data, true); + } else { + + $decoded = unserialize($data); + } + + if (is_array($decoded) == true) { + + $this->cache_data = $decoded; + } else { + + $this->cache_data = array(); + } + + // + // call clean to clean up old entries + // + $this->clean(); + } + } + } + } + + /** + * Destructor + * + * @access public + * + */ + public function __destruct() + { + // + // if there's no cache file set, then there's nothing to do + // + if (strlen($this->cache_file) == 0) { + return; + } + + $fp = fopen($this->cache_file, 'r'); + if ($fp !== false) { + + // + // lock the file + // + flock($fp, LOCK_EX); + + // + // check to see if we have an open shm segment + // + if ($this->_cache_id === false) { + + // + // try opening it again, incase it was created by another + // process in the mean time + // + $this->_cache_id = @shmop_open( + $this->_cache_file_tok, 'w', 0, 0 + ); + if ($this->_cache_id === false) { + + // + // otherwise, create it. + // + $this->_cache_id = @shmop_open( + $this->_cache_file_tok, 'c', 0, $this->cache_size + ); + } + } + + // + // get the size allocated to the segment + // + $allocated = shmop_size($this->_cache_id); + + // + // read the contents + // + $data = trim(shmop_read($this->_cache_id, 0, $allocated)); + + // + // if there was some data + // + if ( ($data !== false) && (strlen($data) > 0) ) { + + // + // unserialize and store the data + // + $c = $this->cache_data; + + $decoded = null; + + if ($this->cache_serializer == 'json') { + + $decoded = json_decode($data, true); + } else { + + $decoded = unserialize($data); + } + + if (is_array($decoded) == true) { + + $this->cache_data = array_merge($c, $decoded); + } + } + + // + // delete the segment + // + shmop_delete($this->_cache_id); + + // + // clean the data + // + $this->clean(); + + // + // clean up and write the data + // + $data = $this->resize(); + if (!is_null($data)) { + + // + // re-create segment + // + $this->_cache_id = @shmop_open( + $this->_cache_file_tok, 'c', 0644, $this->cache_size + ); + if ($this->_cache_id === false) { + return; + } + + $o = shmop_write($this->_cache_id, $data, 0); + } + + // + // close the segment + // + shmop_close($this->_cache_id); + + // + // unlock + // + flock($fp, LOCK_UN); + + // + // close the file + // + fclose($fp); + } + } +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Exception.php b/include/pear/Net/DNS2/Exception.php new file mode 100644 index 0000000000000000000000000000000000000000..b3f809b7f73a39d35f231416de87dcfca48a018c --- /dev/null +++ b/include/pear/Net/DNS2/Exception.php @@ -0,0 +1,132 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Exception.php 197 2013-04-22 00:28:00Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + */ + +/** + * Exception handler used by Net_DNS2 + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * + */ +class Net_DNS2_Exception extends Exception +{ + private $_request; + private $_response; + + /** + * Constructor - overload the constructor so we can pass in the request + * and response object (when it's available) + * + * @param string $message the exception message + * @param int $code the exception code + * @param object $previous the previous Exception object + * @param object $request the Net_DNS2_Packet_Request object for this request + * @param object $response the Net_DNS2_Packet_Response object for this request + * + * @access public + * + */ + public function __construct( + $message = '', + $code = 0, + $previous = null, + Net_DNS2_Packet_Request $request = null, + Net_DNS2_Packet_Response $response = null + ) { + // + // store the request/response objects (if passed) + // + $this->_request = $request; + $this->_response = $response; + + // + // call the parent constructor + // + parent::__construct($message, $code, $previous); + } + + /** + * returns the Net_DNS2_Packet_Request object (if available) + * + * @return Net_DNS2_Packet_Request object + * @access public + * @since function available since release 1.3.1 + * + */ + public function getRequest() + { + return $this->_request; + } + + /** + * returns the Net_DNS2_Packet_Response object (if available) + * + * @return Net_DNS2_Packet_Response object + * @access public + * @since function available since release 1.3.1 + * + */ + public function getResponse() + { + return $this->_response; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Header.php b/include/pear/Net/DNS2/Header.php new file mode 100644 index 0000000000000000000000000000000000000000..3831d75b55e0d40150de1d8673911d4cf21ae678 --- /dev/null +++ b/include/pear/Net/DNS2/Header.php @@ -0,0 +1,282 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Header.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + + +/** + * DNS Packet Header class + * + * This class handles parsing and constructing DNS Packet Headers as defined + * by section 4.1.1 of RFC1035. + * + * DNS header format - RFC1035 section 4.1.1 + * DNS header format - RFC4035 section 3.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * + */ +class Net_DNS2_Header +{ + public $id; // 16 bit - identifier + public $qr; // 1 bit - 0 = query, 1 = response + public $opcode; // 4 bit - op code + public $aa; // 1 bit - Authoritative Answer + public $tc; // 1 bit - TrunCation + public $rd; // 1 bit - Recursion Desired + public $ra; // 1 bit - Recursion Available + public $z; // 1 bit - Reserved + public $ad; // 1 bit - Authentic Data (RFC4035) + public $cd; // 1 bit - Checking Disabled (RFC4035) + public $rcode; // 4 bit - Response code + public $qdcount; // 16 bit - entries in the question section + public $ancount; // 16 bit - resource records in the answer section + public $nscount; // 16 bit - name server rr in the authority records section + public $arcount; // 16 bit - rr's in the additional records section + + /** + * Constructor - builds a new Net_DNS2_Header object + * + * @param mixed &$packet either a Net_DNS2_Packet object or null + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct(Net_DNS2_Packet &$packet = null) + { + if (!is_null($packet)) { + + $this->set($packet); + } else { + + $this->id = $this->nextPacketId(); + $this->qr = Net_DNS2_Lookups::QR_QUERY; + $this->opcode = Net_DNS2_Lookups::OPCODE_QUERY; + $this->aa = 0; + $this->tc = 0; + $this->rd = 1; + $this->ra = 0; + $this->z = 0; + $this->ad = 0; + $this->cd = 0; + $this->rcode = Net_DNS2_Lookups::RCODE_NOERROR; + $this->qdcount = 1; + $this->ancount = 0; + $this->nscount = 0; + $this->arcount = 0; + } + } + + /** + * returns the next available packet id + * + * @return integer + * @access public + * + */ + public function nextPacketId() + { + if (++Net_DNS2_Lookups::$next_packet_id > 65535) { + + Net_DNS2_Lookups::$next_packet_id = 1; + } + + return Net_DNS2_Lookups::$next_packet_id; + } + + /** + * magic __toString() method to return the header as a string + * + * @return string + * @access public + * + */ + public function __toString() + { + $output = ";;\n;; Header:\n"; + + $output .= ";;\t id = " . $this->id . "\n"; + $output .= ";;\t qr = " . $this->qr . "\n"; + $output .= ";;\t opcode = " . $this->opcode . "\n"; + $output .= ";;\t aa = " . $this->aa . "\n"; + $output .= ";;\t tc = " . $this->tc . "\n"; + $output .= ";;\t rd = " . $this->rd . "\n"; + $output .= ";;\t ra = " . $this->ra . "\n"; + $output .= ";;\t z = " . $this->z . "\n"; + $output .= ";;\t ad = " . $this->ad . "\n"; + $output .= ";;\t cd = " . $this->cd . "\n"; + $output .= ";;\t rcode = " . $this->rcode . "\n"; + $output .= ";;\t qdcount = " . $this->qdcount . "\n"; + $output .= ";;\t ancount = " . $this->ancount . "\n"; + $output .= ";;\t nscount = " . $this->nscount . "\n"; + $output .= ";;\t arcount = " . $this->arcount . "\n"; + + return $output; + } + + /** + * constructs a Net_DNS2_Header from a Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet Object + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function set(Net_DNS2_Packet &$packet) + { + // + // the header must be at least 12 bytes long. + // + if ($packet->rdlength < Net_DNS2_Lookups::DNS_HEADER_SIZE) { + + throw new Net_DNS2_Exception( + 'invalid header data provided; to small', + Net_DNS2_Lookups::E_HEADER_INVALID + ); + } + + $offset = 0; + + // + // parse the values + // + $this->id = ord($packet->rdata[$offset]) << 8 | + ord($packet->rdata[++$offset]); + + ++$offset; + $this->qr = (ord($packet->rdata[$offset]) >> 7) & 0x1; + $this->opcode = (ord($packet->rdata[$offset]) >> 3) & 0xf; + $this->aa = (ord($packet->rdata[$offset]) >> 2) & 0x1; + $this->tc = (ord($packet->rdata[$offset]) >> 1) & 0x1; + $this->rd = ord($packet->rdata[$offset]) & 0x1; + + ++$offset; + $this->ra = (ord($packet->rdata[$offset]) >> 7) & 0x1; + $this->z = (ord($packet->rdata[$offset]) >> 6) & 0x1; + $this->ad = (ord($packet->rdata[$offset]) >> 5) & 0x1; + $this->cd = (ord($packet->rdata[$offset]) >> 4) & 0x1; + $this->rcode = ord($packet->rdata[$offset]) & 0xf; + + $this->qdcount = ord($packet->rdata[++$offset]) << 8 | + ord($packet->rdata[++$offset]); + $this->ancount = ord($packet->rdata[++$offset]) << 8 | + ord($packet->rdata[++$offset]); + $this->nscount = ord($packet->rdata[++$offset]) << 8 | + ord($packet->rdata[++$offset]); + $this->arcount = ord($packet->rdata[++$offset]) << 8 | + ord($packet->rdata[++$offset]); + + // + // increment the internal offset + // + $packet->offset += Net_DNS2_Lookups::DNS_HEADER_SIZE; + + return true; + } + + /** + * returns a binary packed DNS Header + * + * @param Net_DNS2_Packet &$packet Object + * + * @return string + * @access public + * + */ + public function get(Net_DNS2_Packet &$packet) + { + $data = pack('n', $this->id) . + chr( + ($this->qr << 7) | ($this->opcode << 3) | + ($this->aa << 2) | ($this->tc << 1) | ($this->rd) + ) . + chr( + ($this->ra << 7) | ($this->ad << 5) | ($this->cd << 4) | $this->rcode + ) . + chr($this->qdcount << 8) . chr($this->qdcount) . + chr($this->ancount << 8) . chr($this->ancount) . + chr($this->nscount << 8) . chr($this->nscount) . + chr($this->arcount << 8) . chr($this->arcount); + + $packet->offset += Net_DNS2_Lookups::DNS_HEADER_SIZE; + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Lookups.php b/include/pear/Net/DNS2/Lookups.php new file mode 100644 index 0000000000000000000000000000000000000000..082ce205002bf4913746d6bdb81049f967856b8d --- /dev/null +++ b/include/pear/Net/DNS2/Lookups.php @@ -0,0 +1,540 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Lookups.php 207 2013-06-13 01:19:55Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +// +// initalize the packet id value +// +Net_DNS2_Lookups::$next_packet_id = mt_rand(0, 65535); + +// +// build the reverse lookup tables; this is just so we don't have to +// have duplicate static content laying around. +// +Net_DNS2_Lookups::$rr_types_by_id = array_flip(Net_DNS2_Lookups::$rr_types_by_name); +Net_DNS2_Lookups::$classes_by_id = array_flip(Net_DNS2_Lookups::$classes_by_name); +Net_DNS2_Lookups::$rr_types_class_to_id = array_flip(Net_DNS2_Lookups::$rr_types_id_to_class); +Net_DNS2_Lookups::$algorithm_name_to_id = array_flip(Net_DNS2_Lookups::$algorithm_id_to_name); +Net_DNS2_Lookups::$digest_name_to_id = array_flip(Net_DNS2_Lookups::$digest_id_to_name); +Net_DNS2_Lookups::$rr_qtypes_by_id = array_flip(Net_DNS2_Lookups::$rr_qtypes_by_name); +Net_DNS2_Lookups::$rr_metatypes_by_id = array_flip(Net_DNS2_Lookups::$rr_metatypes_by_name); +Net_DNS2_Lookups::$protocol_by_id = array_flip(Net_DNS2_Lookups::$protocol_by_name); + +/** + * This class provides simple lookups used througout the Net_DNS2 code + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * + */ +class Net_DNS2_Lookups +{ + /* + * size (in bytes) of a header in a standard DNS packet + */ + const DNS_HEADER_SIZE = 12; + + /* + * max size of a UDP packet + */ + const DNS_MAX_UDP_SIZE = 512; + + /* + * Query/Response flag + */ + const QR_QUERY = 0; // RFC 1035 + const QR_RESPONSE = 1; // RFC 1035 + + /* + * DNS Op Codes + */ + const OPCODE_QUERY = 0; // RFC 1035 + const OPCODE_IQUERY = 1; // RFC 1035, RFC 3425 + const OPCODE_STATUS = 2; // RFC 1035 + const OPCODE_NOTIFY = 4; // RFC 1996 + const OPCODE_UPDATE = 5; // RFC 2136 + + /* + * Resource Record Classes + */ + const RR_CLASS_IN = 1; // RFC 1035 + const RR_CLASS_CH = 3; // RFC 1035 + const RR_CLASS_HS = 4; // RFC 1035 + const RR_CLASS_NONE = 254; // RFC 2136 + const RR_CLASS_ANY = 255; // RFC 1035 + + /* + * DNS Response Codes + */ + const RCODE_NOERROR = 0; // RFC 1035 + const RCODE_FORMERR = 1; // RFC 1035 + const RCODE_SERVFAIL = 2; // RFC 1035 + const RCODE_NXDOMAIN = 3; // RFC 1035 + const RCODE_NOTIMP = 4; // RFC 1035 + const RCODE_REFUSED = 5; // RFC 1035 + const RCODE_YXDOMAIN = 6; // RFC 2136 + const RCODE_YXRRSET = 7; // RFC 2136 + const RCODE_NXRRSET = 8; // RFC 2136 + const RCODE_NOTAUTH = 9; // RFC 2136 + const RCODE_NOTZONE = 10; // RFC 2136 + + // 11-15 reserved + + const RCODE_BADSIG = 16; // RFC 2845 + const RCODE_BADKEY = 17; // RFC 2845 + const RCODE_BADTIME = 18; // RFC 2845 + const RCODE_BADMODE = 19; // RFC 2930 + const RCODE_BADNAME = 20; // RFC 2930 + const RCODE_BADALG = 21; // RFC 2930 + const RCODE_BADTRUNC = 22; // RFC 4635 + + /* + * internal errors codes returned by the exceptions class + */ + const E_NONE = 0; + const E_DNS_FORMERR = self::RCODE_FORMERR; + const E_DNS_SERVFAIL = self::RCODE_SERVFAIL; + const E_DNS_NXDOMAIN = self::RCODE_NXDOMAIN; + const E_DNS_NOTIMP = self::RCODE_NOTIMP; + const E_DNS_REFUSED = self::RCODE_REFUSED; + const E_DNS_YXDOMAIN = self::RCODE_YXDOMAIN; + const E_DNS_YXRRSET = self::RCODE_YXRRSET; + const E_DNS_NXRRSET = self::RCODE_NXRRSET; + const E_DNS_NOTAUTH = self::RCODE_NOTAUTH; + const E_DNS_NOTZONE = self::RCODE_NOTZONE; + + // 11-15 reserved + + const E_DNS_BADSIG = self::RCODE_BADSIG; + const E_DNS_BADKEY = self::RCODE_BADKEY; + const E_DNS_BADTIME = self::RCODE_BADTIME; + const E_DNS_BADMODE = self::RCODE_BADMODE; + const E_DNS_BADNAME = self::RCODE_BADNAME; + const E_DNS_BADALG = self::RCODE_BADALG; + const E_DNS_BADTRUNC = self::RCODE_BADTRUNC; + + // other error conditions + + const E_NS_INVALID_FILE = 200; + const E_NS_INVALID_ENTRY = 201; + const E_NS_FAILED = 202; + + const E_PACKET_INVALID = 300; + const E_PARSE_ERROR = 301; + const E_HEADER_INVALID = 302; + const E_QUESTION_INVALID = 303; + const E_RR_INVALID = 304; + + const E_OPENSSL_ERROR = 400; + const E_OPENSSL_UNAVAIL = 401; + const E_OPENSSL_INV_PKEY = 402; + const E_OPENSSL_INV_ALGO = 403; + + const E_CACHE_UNSUPPORTED = 500; + const E_CACHE_SHM_FILE = 501; + const E_CACHE_SHM_UNAVAIL = 502; + + /* + * DNSSEC Algorithms + */ + const DNSSEC_ALGORITHM_RES = 0; + const DNSSEC_ALGORITHM_RSAMD5 = 1; + const DNSSEC_ALGORITHM_DH = 2; + const DNSSEC_ALGORITHM_DSA = 3; + const DNSSEC_ALGORITHM_ECC = 4; + const DNSSEC_ALGORITHM_RSASHA1 = 5; + const DNSSEC_ALGORITHM_DSANSEC3SHA1 = 6; + const DSNSEC_ALGORITHM_RSASHA1NSEC3SHA1 = 7; + const DNSSEC_ALGORITHM_RSASHA256 = 8; + const DNSSEC_ALGORITHM_RSASHA512 = 10; + const DNSSEC_ALGORITHM_ECCGOST = 12; + const DNSSEC_ALGORITHM_INDIRECT = 252; + const DNSSEC_ALGORITHM_PRIVATEDNS = 253; + const DNSSEC_ALGORITHM_PRIVATEOID = 254; + + /* + * DNSSEC Digest Types + */ + const DNSSEC_DIGEST_RES = 0; + const DNSSEC_DIGEST_SHA1 = 1; + + /* + * The packet id used when sending requests + */ + public static $next_packet_id; + + /* + * Used to map resource record types to their id's, and back + */ + public static $rr_types_by_id = array(); + public static $rr_types_by_name = array( + + 'SIG0' => 0, // RFC 2931 pseudo type + 'A' => 1, // RFC 1035 + 'NS' => 2, // RFC 1035 + 'MD' => 3, // RFC 1035 - obsolete, Not implemented + 'MF' => 4, // RFC 1035 - obsolete, Not implemented + 'CNAME' => 5, // RFC 1035 + 'SOA' => 6, // RFC 1035 + 'MB' => 7, // RFC 1035 - obsolete, Not implemented + 'MG' => 8, // RFC 1035 - obsolete, Not implemented + 'MR' => 9, // RFC 1035 - obsolete, Not implemented + 'NULL' => 10, // RFC 1035 - obsolete, Not implemented + 'WKS' => 11, // RFC 1035 + 'PTR' => 12, // RFC 1035 + 'HINFO' => 13, // RFC 1035 + 'MINFO' => 14, // RFC 1035 - obsolete, Not implemented + 'MX' => 15, // RFC 1035 + 'TXT' => 16, // RFC 1035 + 'RP' => 17, // RFC 1183 + 'AFSDB' => 18, // RFC 1183 + 'X25' => 19, // RFC 1183 + 'ISDN' => 20, // RFC 1183 + 'RT' => 21, // RFC 1183 + 'NSAP' => 22, // RFC 1706 + 'NSAP_PTR' => 23, // RFC 1348 - obsolete, Not implemented + 'SIG' => 24, // RFC 2535 + 'KEY' => 25, // RFC 2535, RFC 2930 + 'PX' => 26, // RFC 2163 + 'GPOS' => 27, // RFC 1712 - Not implemented + 'AAAA' => 28, // RFC 3596 + 'LOC' => 29, // RFC 1876 + 'NXT' => 30, // RFC 2065, obsoleted by by RFC 3755 + 'EID' => 31, // [Patton][Patton1995] + 'NIMLOC' => 32, // [Patton][Patton1995] + 'SRV' => 33, // RFC 2782 + 'ATMA' => 34, // Windows only + 'NAPTR' => 35, // RFC 2915 + 'KX' => 36, // RFC 2230 + 'CERT' => 37, // RFC 4398 + 'A6' => 38, // downgraded to experimental by RFC 3363 + 'DNAME' => 39, // RFC 2672 + 'SINK' => 40, // Not implemented + 'OPT' => 41, // RFC 2671 + 'APL' => 42, // RFC 3123 + 'DS' => 43, // RFC 4034 + 'SSHFP' => 44, // RFC 4255 + 'IPSECKEY' => 45, // RFC 4025 + 'RRSIG' => 46, // RFC 4034 + 'NSEC' => 47, // RFC 4034 + 'DNSKEY' => 48, // RFC 4034 + 'DHCID' => 49, // RFC 4701 + 'NSEC3' => 50, // RFC 5155 + 'NSEC3PARAM' => 51, // RFC 5155 + 'TLSA' => 52, // RFC 6698 + + // 52 - 54 unassigned + + 'HIP' => 55, // RFC 5205 + 'NINFO' => 56, // Not implemented + 'RKEY' => 57, // Not implemented + 'TALINK' => 58, // IETF (draft-barwood-dnsop-ds-publish-02) + 'CDS' => 59, // IETF (draft-barwood-dnsop-ds-publish-02) + + // 60 - 98 unassigned + + 'SPF' => 99, // RFC 4408 + 'UINFO' => 100, // no RFC, Not implemented + 'UID' => 101, // no RFC, Not implemented + 'GID' => 102, // no RFC, Not implemented + 'UNSPEC' => 103, // no RFC, Not implemented + 'NID' => 104, // RFC 6742 + 'L32' => 105, // RFC 6742 + 'L64' => 106, // RFC 6742 + 'LP' => 107, // RFC 6742 + + // 108 - 248 unassigned + + 'TKEY' => 249, // RFC 2930 + 'TSIG' => 250, // RFC 2845 + 'IXFR' => 251, // RFC 1995 - only a full (AXFR) is supported + 'AXFR' => 252, // RFC 1035 + 'MAILB' => 253, // RFC 883, Not implemented + 'MAILA' => 254, // RFC 973, Not implemented + 'ANY' => 255, // RFC 1035 - we support both 'ANY' and '*' + 'URI' => 256, // tools.ietf.org/html/draft-faltstrom-uri-06 + 'CAA' => 257, // tools.ietf.org/html/draft-ietf-pkix-caa-03 + + // 258 - 32767 unassigned + + 'TA' => 32768, // same as DS + 'DLV' => 32769 // RFC 4431 + ); + + /* + * Qtypes and Metatypes - defined in RFC2929 section 3.1 + */ + public static $rr_qtypes_by_id = array(); + public static $rr_qtypes_by_name = array( + + 'IXFR' => 251, // RFC 1995 - only a full (AXFR) is supported + 'AXFR' => 252, // RFC 1035 + 'MAILB' => 253, // RFC 883, Not implemented + 'MAILA' => 254, // RFC 973, Not implemented + 'ANY' => 255 // RFC 1035 - we support both 'ANY' and '*' + ); + + public static $rr_metatypes_by_id = array(); + public static $rr_metatypes_by_name = array( + + 'OPT' => 41, // RFC 2671 + 'TKEY' => 249, // RFC 2930 + 'TSIG' => 250 // RFC 2845 + ); + + /* + * used to map resource record id's to RR class names + */ + public static $rr_types_class_to_id = array(); + public static $rr_types_id_to_class = array( + + 1 => 'Net_DNS2_RR_A', + 2 => 'Net_DNS2_RR_NS', + 5 => 'Net_DNS2_RR_CNAME', + 6 => 'Net_DNS2_RR_SOA', + 11 => 'Net_DNS2_RR_WKS', + 12 => 'Net_DNS2_RR_PTR', + 13 => 'Net_DNS2_RR_HINFO', + 15 => 'Net_DNS2_RR_MX', + 16 => 'Net_DNS2_RR_TXT', + 17 => 'Net_DNS2_RR_RP', + 18 => 'Net_DNS2_RR_AFSDB', + 19 => 'Net_DNS2_RR_X25', + 20 => 'Net_DNS2_RR_ISDN', + 21 => 'Net_DNS2_RR_RT', + 22 => 'Net_DNS2_RR_NSAP', + 24 => 'Net_DNS2_RR_SIG', + 25 => 'Net_DNS2_RR_KEY', + 26 => 'Net_DNS2_RR_PX', + 28 => 'Net_DNS2_RR_AAAA', + 29 => 'Net_DNS2_RR_LOC', + 31 => 'Net_DNS2_RR_EID', + 32 => 'Net_DNS2_RR_NIMLOC', + 33 => 'Net_DNS2_RR_SRV', + 34 => 'Net_DNS2_RR_ATMA', + 35 => 'Net_DNS2_RR_NAPTR', + 36 => 'Net_DNS2_RR_KX', + 37 => 'Net_DNS2_RR_CERT', + 39 => 'Net_DNS2_RR_DNAME', + 41 => 'Net_DNS2_RR_OPT', + 42 => 'Net_DNS2_RR_APL', + 43 => 'Net_DNS2_RR_DS', + 44 => 'Net_DNS2_RR_SSHFP', + 45 => 'Net_DNS2_RR_IPSECKEY', + 46 => 'Net_DNS2_RR_RRSIG', + 47 => 'Net_DNS2_RR_NSEC', + 48 => 'Net_DNS2_RR_DNSKEY', + 49 => 'Net_DNS2_RR_DHCID', + 50 => 'Net_DNS2_RR_NSEC3', + 51 => 'Net_DNS2_RR_NSEC3PARAM', + 52 => 'Net_DNS2_RR_TLSA', + 55 => 'Net_DNS2_RR_HIP', + 58 => 'Net_DNS2_RR_TALINK', + 59 => 'Net_DNS2_RR_CDS', + 99 => 'Net_DNS2_RR_SPF', + 104 => 'Net_DNS2_RR_NID', + 105 => 'Net_DNS2_RR_L32', + 106 => 'Net_DNS2_RR_L64', + 107 => 'Net_DNS2_RR_LP', + 249 => 'Net_DNS2_RR_TKEY', + 250 => 'Net_DNS2_RR_TSIG', + + // 251 - IXFR - handled as a full zone transfer (252) + // 252 - AXFR - handled as a function call + // 255 - ANY - used only for queries + + 256 => 'Net_DNS2_RR_URI', + 257 => 'Net_DNS2_RR_CAA', + 32768 => 'Net_DNS2_RR_TA', + 32769 => 'Net_DNS2_RR_DLV' + ); + + /* + * used to map resource record class names to their id's, and back + */ + public static $classes_by_id = array(); + public static $classes_by_name = array( + + 'IN' => self::RR_CLASS_IN, // RFC 1035 + 'CH' => self::RR_CLASS_CH, // RFC 1035 + 'HS' => self::RR_CLASS_HS, // RFC 1035 + 'NONE' => self::RR_CLASS_NONE, // RFC 2136 + 'ANY' => self::RR_CLASS_ANY // RFC 1035 + ); + + /* + * maps response codes to error messages + */ + public static $result_code_messages = array( + + self::RCODE_NOERROR => 'The request completed successfully.', + self::RCODE_FORMERR => 'The name server was unable to interpret the query.', + self::RCODE_SERVFAIL => 'The name server was unable to process this query due to a problem with the name server.', + self::RCODE_NXDOMAIN => 'The domain name referenced in the query does not exist.', + self::RCODE_NOTIMP => 'The name server does not support the requested kind of query.', + self::RCODE_REFUSED => 'The name server refuses to perform the specified operation for policy reasons.', + self::RCODE_YXDOMAIN => 'Name Exists when it should not.', + self::RCODE_YXRRSET => 'RR Set Exists when it should not.', + self::RCODE_NXRRSET => 'RR Set that should exist does not.', + self::RCODE_NOTAUTH => 'Server Not Authoritative for zone.', + self::RCODE_NOTZONE => 'Name not contained in zone.', + + self::RCODE_BADSIG => 'TSIG Signature Failure.', + self::RCODE_BADKEY => 'Key not recognized.', + self::RCODE_BADTIME => 'Signature out of time window.', + self::RCODE_BADMODE => 'Bad TKEY Mode.', + self::RCODE_BADNAME => 'Duplicate key name.', + self::RCODE_BADALG => 'Algorithm not supported.', + self::RCODE_BADTRUNC => 'Bad truncation.' + ); + + /* + * maps DNS SEC alrorithms to their mnemonics + */ + public static $algorithm_name_to_id = array(); + public static $algorithm_id_to_name = array( + + self::DNSSEC_ALGORITHM_RES => 'RES', + self::DNSSEC_ALGORITHM_RSAMD5 => 'RSAMD5', + self::DNSSEC_ALGORITHM_DH => 'DH', + self::DNSSEC_ALGORITHM_DSA => 'DSA', + self::DNSSEC_ALGORITHM_ECC => 'ECC', + self::DNSSEC_ALGORITHM_RSASHA1 => 'RSASHA1', + self::DNSSEC_ALGORITHM_DSANSEC3SHA1 => 'DSA-NSEC3-SHA1', + self::DSNSEC_ALGORITHM_RSASHA1NSEC3SHA1 => 'RSASHA1-NSEC3-SHA1', + self::DNSSEC_ALGORITHM_RSASHA256 => 'RSASHA256', + self::DNSSEC_ALGORITHM_RSASHA512 => 'RSASHA512', + self::DNSSEC_ALGORITHM_ECCGOST => 'ECC-GOST', + self::DNSSEC_ALGORITHM_INDIRECT => 'INDIRECT', + self::DNSSEC_ALGORITHM_PRIVATEDNS => 'PRIVATEDNS', + self::DNSSEC_ALGORITHM_PRIVATEOID => 'PRIVATEOID' + ); + + /* + * maps DNSSEC digest types to their mnemonics + */ + public static $digest_name_to_id = array(); + public static $digest_id_to_name = array( + + self::DNSSEC_DIGEST_RES => 'RES', + self::DNSSEC_DIGEST_SHA1 => 'SHA-1' + ); + + /* + * Protocols names - RFC 1010 + */ + public static $protocol_by_id = array(); + public static $protocol_by_name = array( + + 'ICMP' => 1, + 'IGMP' => 2, + 'GGP' => 3, + 'ST' => 5, + 'TCP' => 6, + 'UCL' => 7, + 'EGP' => 8, + 'IGP' => 9, + 'BBN-RCC-MON' => 10, + 'NVP-II' => 11, + 'PUP' => 12, + 'ARGUS' => 13, + 'EMCON' => 14, + 'XNET' => 15, + 'CHAOS' => 16, + 'UDP' => 17, + 'MUX' => 18, + 'DCN-MEAS' => 19, + 'HMP' => 20, + 'PRM' => 21, + 'XNS-IDP' => 22, + 'TRUNK-1' => 23, + 'TRUNK-2' => 24, + 'LEAF-1' => 25, + 'LEAF-2' => 26, + 'RDP' => 27, + 'IRTP' => 28, + 'ISO-TP4' => 29, + 'NETBLT' => 30, + 'MFE-NSP' => 31, + 'MERIT-INP' => 32, + 'SEP' => 33, + // 34 - 60 - Unassigned + // 61 - any host internal protocol + 'CFTP' => 62, + // 63 - any local network + 'SAT-EXPAK' => 64, + 'MIT-SUBNET' => 65, + 'RVD' => 66, + 'IPPC' => 67, + // 68 - any distributed file system + 'SAT-MON' => 69, + // 70 - Unassigned + 'IPCV' => 71, + // 72 - 75 - Unassigned + 'BR-SAT-MON' => 76, + // 77 - Unassigned + 'WB-MON' => 78, + 'WB-EXPAK' => 79 + // 80 - 254 - Unassigned + // 255 - Reserved + ); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Packet.php b/include/pear/Net/DNS2/Packet.php new file mode 100644 index 0000000000000000000000000000000000000000..12c67063c3f741d0283d30c776b43c4027e54935 --- /dev/null +++ b/include/pear/Net/DNS2/Packet.php @@ -0,0 +1,449 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Packet.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + * This file contains code based off the Net::DNS Perl module by + * Michael Fuhr. + * + * This is the copyright notice from the PERL Net::DNS module: + * + * Copyright (c) 1997-2000 Michael Fuhr. All rights reserved. This + * program is free software; you can redistribute it and/or modify it + * under the same terms as Perl itself. + * + */ + +/** + * This is the base class that holds a standard DNS packet. + * + * The Net_DNS2_Packet_Request and Net_DNS2_Packet_Response classes extend this + * class. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet_Request, Net_DNS2_Packet_Response + * + */ +class Net_DNS2_Packet +{ + /* + * the full binary data and length for this packet + */ + public $rdata; + public $rdlength; + + /* + * the offset pointer used when building/parsing packets + */ + public $offset = 0; + + /* + * Net_DNS2_Header object with the DNS packet header + */ + public $header; + + /* + * array of Net_DNS2_Question objects + * + * used as "zone" for updates per RFC2136 + * + */ + public $question = array(); + + /* + * array of Net_DNS2_RR Objects for Answers + * + * used as "prerequisite" for updates per RFC2136 + * + */ + public $answer = array(); + + /* + * array of Net_DNS2_RR Objects for Authority + * + * used as "update" for updates per RFC2136 + * + */ + public $authority = array(); + + /* + * array of Net_DNS2_RR Objects for Addtitional + */ + public $additional = array(); + + /* + * array of compressed labeles + */ + private $_compressed = array(); + + /** + * magic __toString() method to return the Net_DNS2_Packet as a string + * + * @return string + * @access public + * + */ + public function __toString() + { + $output = $this->header->__toString(); + + foreach ($this->question as $x) { + + $output .= $x->__toString() . "\n"; + } + foreach ($this->answer as $x) { + + $output .= $x->__toString() . "\n"; + } + foreach ($this->authority as $x) { + + $output .= $x->__toString() . "\n"; + } + foreach ($this->additional as $x) { + + $output .= $x->__toString() . "\n"; + } + + return $output; + } + + /** + * returns a full binary DNS packet + * + * @return string + * @throws Net_DNS2_Exception + * @access public + * + */ + public function get() + { + $data = $this->header->get($this); + + foreach ($this->question as $x) { + + $data .= $x->get($this); + } + foreach ($this->answer as $x) { + + $data .= $x->get($this); + } + foreach ($this->authority as $x) { + + $data .= $x->get($this); + } + foreach ($this->additional as $x) { + + $data .= $x->get($this); + } + + return $data; + } + + /** + * applies a standard DNS name compression on the given name/offset + * + * This logic was based on the Net::DNS::Packet::dn_comp() function (nolint) + * by Michanel Fuhr + * + * @param string $name the name to be compressed + * @param integer &$offset the offset into the given packet object + * + * @return string + * @access public + * + */ + public function compress($name, &$offset) + { + $names = explode('.', $name); + $compname = ''; + + while (!empty($names)) { + + $dname = join('.', $names); + + if (isset($this->_compressed[$dname])) { + + $compname .= pack('n', 0xc000 | $this->_compressed[$dname]); + $offset += 2; + + break; + } + + $this->_compressed[$dname] = $offset; + $first = array_shift($names); + + $length = strlen($first); + if ($length <= 0) { + continue; + } + + // + // truncate see RFC1035 2.3.1 + // + if ($length > 63) { + + $length = 63; + $first = substr($first, 0, $length); + } + + $compname .= pack('Ca*', $length, $first); + $offset += $length + 1; + } + + if (empty($names)) { + + $compname .= pack('C', 0); + $offset++; + } + + return $compname; + } + + /** + * applies a standard DNS name compression on the given name/offset + * + * This logic was based on the Net::DNS::Packet::dn_comp() function (nolint) + * by Michanel Fuhr + * + * @param string $name the name to be compressed + * + * @return string + * @access public + * + */ + public static function pack($name) + { + $offset = 0; + $names = explode('.', $name); + $compname = ''; + + while (!empty($names)) { + + $first = array_shift($names); + $length = strlen($first); + + $compname .= pack('Ca*', $length, $first); + $offset += $length + 1; + } + + $compname .= "\0"; + $offset++; + + return $compname; + } + + /** + * expands the domain name stored at a given offset in a DNS Packet + * + * This logic was based on the Net::DNS::Packet::dn_expand() function (nolint) + * by Michanel Fuhr + * + * @param Net_DNS2_Packet &$packet the DNS packet to look in for the domain name + * @param integer &$offset the offset into the given packet object + * + * @return mixed either the domain name or null if it's not found. + * @access public + * + */ + public static function expand(Net_DNS2_Packet &$packet, &$offset) + { + $name = ''; + + while (1) { + if ($packet->rdlength < ($offset + 1)) { + return null; + } + + $xlen = ord($packet->rdata[$offset]); + if ($xlen == 0) { + + ++$offset; + break; + + } else if (($xlen & 0xc0) == 0xc0) { + if ($packet->rdlength < ($offset + 2)) { + + return null; + } + + $ptr = ord($packet->rdata[$offset]) << 8 | + ord($packet->rdata[$offset+1]); + $ptr = $ptr & 0x3fff; + + $name2 = Net_DNS2_Packet::expand($packet, $ptr); + if (is_null($name2)) { + + return null; + } + + $name .= $name2; + $offset += 2; + + break; + } else { + ++$offset; + + if ($packet->rdlength < ($offset + $xlen)) { + + return null; + } + + $elem = ''; + $elem = substr($packet->rdata, $offset, $xlen); + $name .= $elem . '.'; + $offset += $xlen; + } + } + + return trim($name, '.'); + } + + /** + * parses a domain label from a DNS Packet at the given offset + * + * @param Net_DNS2_Packet &$packet the DNS packet to look in for the domain name + * @param integer &$offset the offset into the given packet object + * + * @return mixed either the domain name or null if it's not found. + * @access public + * + */ + public static function label(Net_DNS2_Packet &$packet, &$offset) + { + $name = ''; + + if ($packet->rdlength < ($offset + 1)) { + + return null; + } + + $xlen = ord($packet->rdata[$offset]); + ++$offset; + + if (($xlen + $offset) > $packet->rdlength) { + + $name = substr($packet->rdata, $offset); + $offset = $packet->rdlength; + } else { + + $name = substr($packet->rdata, $offset, $xlen); + $offset += $xlen; + } + + return $name; + } + + /** + * copies the contents of the given packet, to the local packet object. this + * function intentionally ignores some of the packet data. + * + * @param Net_DNS2_Packet $packet the DNS packet to copy the data from + * + * @return boolean + * @access public + * + */ + public function copy(Net_DNS2_Packet $packet) + { + $this->header = $packet->header; + $this->question = $packet->question; + $this->answer = $packet->answer; + $this->authority = $packet->authority; + $this->additional = $packet->additional; + + return true; + } + + /** + * resets the values in the current packet object + * + * @return boolean + * @access public + * + */ + public function reset() + { + $this->header->id = $this->header->nextPacketId(); + $this->rdata = ''; + $this->rdlength = 0; + $this->offset = 0; + $this->answer = array(); + $this->authority = array(); + $this->additional = array(); + $this->_compressed = array(); + + return true; + } + + /** + * formats an IPv6 IP address in the preferred format + * + * @param string $address The IPv6 IP address to format + * + * @return string The IPv6 IP address formatted in the new format + * @access public + * @deprecated function deprecated in 1.1.3 + * + */ + public static function formatIPv6($address) + { + return Net_DNS2::expandIPv6($address); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Packet/Request.php b/include/pear/Net/DNS2/Packet/Request.php new file mode 100644 index 0000000000000000000000000000000000000000..117fc7a98e03b5f1ec585a42d880dd6a80063056 --- /dev/null +++ b/include/pear/Net/DNS2/Packet/Request.php @@ -0,0 +1,221 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Request.php 155 2012-05-06 23:45:23Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + + +/** + * This class handles building new DNS request packets; packets used for DNS + * queries and updates. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Packet_Request extends Net_DNS2_Packet +{ + /** + * Constructor - builds a new Net_DNS2_Packet_Request object + * + * @param string $name the domain name for the packet + * @param string $type the DNS RR type for the packet + * @param string $class the DNS class for the packet + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct($name, $type = null, $class = null) + { + $this->set($name, $type, $class); + } + + /** + * builds a new Net_DNS2_Packet_Request object + * + * @param string $name the domain name for the packet + * @param string $type the DNS RR type for the packet + * @param string $class the DNS class for the packet + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function set($name, $type = 'A', $class = 'IN') + { + // + // generate a new header + // + $this->header = new Net_DNS2_Header; + + // + // add a new question + // + $q = new Net_DNS2_Question(); + + $name = trim(strtolower($name), " \t\n\r\0\x0B."); + $type = strtoupper(trim($type)); + $class = strtoupper(trim($class)); + + // + // check that the input string has some data in it + // + if (empty($name)) { + + throw new Net_DNS2_Exception( + 'empty query string provided', + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + + // + // if the type is "*", rename it to "ANY"- both are acceptable. + // + if ($type == '*') { + + $type = 'ANY'; + } + + // + // check that the type and class are valid + // + if ( (!isset(Net_DNS2_Lookups::$rr_types_by_name[$type])) + || (!isset(Net_DNS2_Lookups::$classes_by_name[$class])) + ) { + throw new Net_DNS2_Exception( + 'invalid type (' . $type . ') or class (' . $class . ') specified.', + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + + // + // if it's a PTR request for an IP address, then make sure we tack on + // the arpa domain + // + if ($type == 'PTR') { + + if (Net_DNS2::isIPv4($name) == true) { + + // + // IPv4 + // + $name = implode('.', array_reverse(explode('.', $name))); + $name .= '.in-addr.arpa'; + + } else if (Net_DNS2::isIPv6($name) == true) { + + // + // IPv6 + // + $e = Net_DNS2::expandIPv6($name); + if ($e !== false) { + + $name = implode( + '.', array_reverse(str_split(str_replace(':', '', $e))) + ); + + $name .= '.ip6.arpa'; + + } else { + + throw new Net_DNS2_Exception( + 'unsupported PTR value: ' . $name, + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + + } else if (preg_match('/arpa$/', $name) == true) { + + // + // an already formatted IPv4 or IPv6 address in the arpa domain + // + + } else { + + throw new Net_DNS2_Exception( + 'unsupported PTR value: ' . $name, + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + } + + // + // store the data + // + $q->qname = $name; + $q->qtype = $type; + $q->qclass = $class; + + $this->question[] = $q; + + // + // the answer, authority and additional are empty; they can be modified + // after the request is created for UPDATE requests if needed. + // + $this->answer = array(); + $this->authority = array(); + $this->additional = array(); + + return true; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Packet/Response.php b/include/pear/Net/DNS2/Packet/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..a2cd1f281cde12e6bd967586568f613dab43918d --- /dev/null +++ b/include/pear/Net/DNS2/Packet/Response.php @@ -0,0 +1,189 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Response.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + + +/** + * This class handles building new DNS response packets; it parses binary packed + * packets that come off the wire + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Packet_Response extends Net_DNS2_Packet +{ + /* + * The name servers that this response came from + */ + public $answer_from; + + /* + * The socket type the answer came from (TCP/UDP) + */ + public $answer_socket_type; + + /** + * Constructor - builds a new Net_DNS2_Packet_Response object + * + * @param string $data binary DNS packet + * @param integer $size the length of the DNS packet + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct($data, $size) + { + $this->set($data, $size); + } + + /** + * builds a new Net_DNS2_Packet_Response object + * + * @param string $data binary DNS packet + * @param integer $size the length of the DNS packet + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function set($data, $size) + { + // + // store the full packet + // + $this->rdata = $data; + $this->rdlength = $size; + + // + // parse the header + // + // we don't bother checking the size earlier, because the first thing the + // header class does, is check the size and throw and exception if it's + // invalid. + // + $this->header = new Net_DNS2_Header($this); + + // + // if the truncation bit is set, then just return right here, because the + // rest of the packet is probably empty; and there's no point in processing + // anything else. + // + // we also don't need to worry about checking to see if the the header is + // null or not, since the Net_DNS2_Header() constructor will throw an + // exception if the packet is invalid. + // + if ($this->header->tc == 1) { + + return false; + } + + // + // parse the questions + // + for ($x = 0; $x < $this->header->qdcount; ++$x) { + + $this->question[$x] = new Net_DNS2_Question($this); + } + + // + // parse the answers + // + for ($x = 0; $x < $this->header->ancount; ++$x) { + + $o = Net_DNS2_RR::parse($this); + if (!is_null($o)) { + + $this->answer[] = $o; + } + } + + // + // parse the authority section + // + for ($x = 0; $x < $this->header->nscount; ++$x) { + + $o = Net_DNS2_RR::parse($this); + if (!is_null($o)) { + + $this->authority[] = $o; + } + } + + // + // parse the additional section + // + for ($x = 0; $x < $this->header->arcount; ++$x) { + + $o = Net_DNS2_RR::parse($this); + if (!is_null($o)) { + + $this->additional[] = $o; + } + } + + return true; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/PrivateKey.php b/include/pear/Net/DNS2/PrivateKey.php new file mode 100644 index 0000000000000000000000000000000000000000..1dca1ce6d225237e48d74d72aca667107391c6ee --- /dev/null +++ b/include/pear/Net/DNS2/PrivateKey.php @@ -0,0 +1,422 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: PrivateKey.php 133 2011-12-03 23:42:24Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.1.0 + * + */ + +/** + * SSL Private Key container class + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * + */ +class Net_DNS2_PrivateKey +{ + /* + * the filename that was loaded; stored for reference + */ + public $filename; + + /* + * the keytag for the signature + */ + public $keytag; + + /* + * the sign name for the signature + */ + public $signname; + + /* + * the algorithm used for the signature + */ + public $algorithm; + + /* + * the key format fo the signature + */ + public $key_format; + + /* + * the openssl private key id + */ + public $instance; + + /* + * RSA: modulus + */ + private $_modulus; + + /* + * RSA: public exponent + */ + private $_public_exponent; + + /* + * RSA: rivate exponent + */ + private $_private_exponent; + + /* + * RSA: prime1 + */ + private $_prime1; + + /* + * RSA: prime2 + */ + private $_prime2; + + /* + * RSA: exponent 1 + */ + private $_exponent1; + + /* + * RSA: exponent 2 + */ + private $_exponent2; + + /* + * RSA: coefficient + */ + private $_coefficient; + + /* + * DSA: prime + */ + //private $_prime; + + /* + * DSA: subprime + */ + //private $_subprime; + + /* + * DSA: base + */ + //private $_base; + + /* + * DSA: private value + */ + //private $_private_value; + + /* + * DSA: public value + */ + //private $_public_value; + + /** + * Constructor - base constructor the private key container class + * + * @param string $file path to a private-key file to parse and load + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct($file = null) + { + if (isset($file)) { + $this->parseFile($file); + } + } + + /** + * parses a private key file generated by dnssec-keygen + * + * @param string $file path to a private-key file to parse and load + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function parseFile($file) + { + // + // check for OpenSSL + // + if (extension_loaded('openssl') === false) { + + throw new Net_DNS2_Exception( + 'the OpenSSL extension is required to use parse private key.', + Net_DNS2_Lookups::E_OPENSSL_UNAVAIL + ); + } + + // + // check to make sure the file exists + // + if (is_readable($file) == false) { + + throw new Net_DNS2_Exception( + 'invalid private key file: ' . $file, + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + + // + // get the base filename, and parse it for the local value + // + $keyname = basename($file); + if (strlen($keyname) == 0) { + + throw new Net_DNS2_Exception( + 'failed to get basename() for: ' . $file, + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + + // + // parse the keyname + // + if (preg_match("/K(.*)\.\+(\d{3})\+(\d*)\.private/", $keyname, $matches)) { + + $this->signname = $matches[1]; + $this->algorithm = intval($matches[2]); + $this->keytag = intval($matches[3]); + + } else { + + throw new Net_DNS2_Exception( + 'file ' . $keyname . ' does not look like a private key file!', + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + + // + // read all the data from the + // + $data = file($file, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES); + if (count($data) == 0) { + + throw new Net_DNS2_Exception( + 'file ' . $keyname . ' is empty!', + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + + foreach ($data as $line) { + + list($key, $value) = explode(':', $line); + + $key = trim($key); + $value = trim($value); + + switch(strtolower($key)) { + + case 'private-key-format': + $this->_key_format = $value; + break; + + case 'algorithm': + if ($this->algorithm != $value) { + throw new Net_DNS2_Exception( + 'Algorithm mis-match! filename is ' . $this->algorithm . + ', contents say ' . $value, + Net_DNS2_Lookups::E_OPENSSL_INV_ALGO + ); + } + break; + + // + // RSA + // + case 'modulus': + $this->_modulus = $value; + break; + + case 'publicexponent': + $this->_public_exponent = $value; + break; + + case 'privateexponent': + $this->_private_exponent = $value; + break; + + case 'prime1': + $this->_prime1 = $value; + break; + + case 'prime2': + $this->_prime2 = $value; + break; + + case 'exponent1': + $this->_exponent1 = $value; + break; + + case 'exponent2': + $this->_exponent2 = $value; + break; + + case 'coefficient': + $this->_coefficient = $value; + break; + + // + // DSA - this won't work in PHP until the OpenSSL extension is better + // + /*case 'prime(p)': + $this->_prime = $value; + break; + + case 'subprime(q)': + $this->_subprime = $value; + break; + + case 'base(g)': + $this->_base = $value; + break; + + case 'private_value(x)': + $this->_private_value = $value; + break; + + case 'public_value(y)': + $this->_public_value = $value; + break; + */ + default: + throw new Net_DNS2_Exception( + 'unknown private key data: ' . $key . ': ' . $value, + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + } + + // + // generate the private key + // + $args = array(); + + switch($this->algorithm) { + + // + // RSA + // + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: + + $args = array( + + 'rsa' => array( + + 'n' => base64_decode($this->_modulus), + 'e' => base64_decode($this->_public_exponent), + 'd' => base64_decode($this->_private_exponent), + 'p' => base64_decode($this->_prime1), + 'q' => base64_decode($this->_prime2), + 'dmp1' => base64_decode($this->_exponent1), + 'dmq1' => base64_decode($this->_exponent2), + 'iqmp' => base64_decode($this->_coefficient) + ) + ); + + break; + + // + // DSA - this won't work in PHP until the OpenSSL extension is better + // + /*case Net_DNS2_Lookups::DNSSEC_ALGORITHM_DSA: + + $args = array( + + 'dsa' => array( + + 'p' => base64_decode($this->_prime), + 'q' => base64_decode($this->_subprime), + 'g' => base64_decode($this->_base), + 'priv_key' => base64_decode($this->_private_value), + 'pub_key' => base64_decode($this->_public_value) + ) + ); + + break; + */ + default: + throw new Net_DNS2_Exception( + 'we only currently support RSAMD5 and RSASHA1 encryption.', + Net_DNS2_Lookups::E_OPENSSL_INV_PKEY + ); + } + + // + // generate and store the key + // + $this->instance = openssl_pkey_new($args); + if ($this->instance === false) { + throw new Net_DNS2_Exception( + openssl_error_string(), + Net_DNS2_Lookups::E_OPENSSL_ERROR + ); + } + + // + // store the filename incase we need it for something + // + $this->filename = $file; + + return true; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Question.php b/include/pear/Net/DNS2/Question.php new file mode 100644 index 0000000000000000000000000000000000000000..ddae4607b85b84a65f4be5528864efb421883f4b --- /dev/null +++ b/include/pear/Net/DNS2/Question.php @@ -0,0 +1,244 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Question.php 124 2011-12-02 23:23:15Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * This class handles parsing and constructing the question sectino of DNS + * packets. + * + * This is referred to as the "zone" for update per RFC2136 + * + * DNS question format - RFC1035 section 4.1.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Packet + * + */ +class Net_DNS2_Question +{ + /* + * The name of the question + * + * referred to as "zname" for updates per RFC2136 + * + */ + public $qname; + + /* + * The RR type for the questino + * + * referred to as "ztype" for updates per RFC2136 + * + */ + public $qtype; + + /* + * The RR class for the questino + * + * referred to as "zclass" for updates per RFC2136 + * + */ + public $qclass; + + /** + * Constructor - builds a new Net_DNS2_Question object + * + * @param mixed &$packet either a Net_DNS2_Packet object, or null to + * build an empty object + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct(Net_DNS2_Packet &$packet = null) + { + if (!is_null($packet)) { + + $this->set($packet); + } else { + + $this->qname = ''; + $this->qtype = 'A'; + $this->qclass = 'IN'; + } + } + + /** + * magic __toString() function to return the Net_DNS2_Question object as a string + * + * @return string + * @access public + * + */ + public function __toString() + { + return ";;\n;; Question:\n;;\t " . $this->qname . '. ' . + $this->qtype . ' ' . $this->qclass . "\n"; + } + + /** + * builds a new Net_DNS2_Header object from a Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet object + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function set(Net_DNS2_Packet &$packet) + { + // + // expand the name + // + $this->qname = $packet->expand($packet, $packet->offset); + if ($packet->rdlength < ($packet->offset + 4)) { + + throw new Net_DNS2_Exception( + 'invalid question section: to small', + Net_DNS2_Lookups::E_QUESTION_INVALID + ); + } + + // + // unpack the type and class + // + $type = ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + $class = ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + + // + // validate it + // + $type_name = Net_DNS2_Lookups::$rr_types_by_id[$type]; + $class_name = Net_DNS2_Lookups::$classes_by_id[$class]; + + if ( (!isset($type_name)) || (!isset($class_name)) ) { + + throw new Net_DNS2_Exception( + 'invalid question section: invalid type (' . $type . + ') or class (' . $class . ') specified.', + Net_DNS2_Lookups::E_QUESTION_INVALID + ); + } + + // + // store it + // + $this->qtype = $type_name; + $this->qclass = $class_name; + + return true; + } + + /** + * returns a binary packed Net_DNS2_Question object + * + * @param Net_DNS2_Packet &$packet the Net_DNS2_Packet object this question is + * part of. This needs to be passed in so that + * the compressed qname value can be packed in + * with the names of the other parts of the + * packet. + * + * @return string + * @throws Net_DNS2_Exception + * @access public + * + */ + public function get(Net_DNS2_Packet &$packet) + { + // + // validate the type and class + // + $type = Net_DNS2_Lookups::$rr_types_by_name[$this->qtype]; + $class = Net_DNS2_Lookups::$classes_by_name[$this->qclass]; + + if ( (!isset($type)) || (!isset($class)) ) { + + throw new Net_DNS2_Exception( + 'invalid question section: invalid type (' . $this->qtype . + ') or class (' . $this->qclass . ') specified.', + Net_DNS2_Lookups::E_QUESTION_INVALID + ); + } + + $data = $packet->compress($this->qname, $packet->offset); + + $data .= chr($type << 8) . chr($type) . chr($class << 8) . chr($class); + $packet->offset += 4; + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR.php b/include/pear/Net/DNS2/RR.php new file mode 100644 index 0000000000000000000000000000000000000000..55ee7f70a1faba8401dfee7f7aeee97351e686a3 --- /dev/null +++ b/include/pear/Net/DNS2/RR.php @@ -0,0 +1,641 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: RR.php 188 2013-03-31 01:25:46Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + + +/** + * This is the base class for DNS Resource Records + * + * Each resource record type (defined in RR/*.php) extends this class for + * base functionality. + * + * This class handles parsing and constructing the common parts of the DNS + * resource records, while the RR specific functionality is handled in each + * child class. + * + * DNS resource record format - RFC1035 section 4.1.3 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / / + * / NAME / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | CLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TTL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * + */ +abstract class Net_DNS2_RR +{ + /* + * The name of the resource record + */ + public $name; + + /* + * The resource record type + */ + public $type; + + /* + * The resouce record class + */ + public $class; + + /* + * The time to live for this resource record + */ + public $ttl; + + /* + * The length of the rdata field + */ + public $rdlength; + + /* + * The resource record specific data as a packed binary string + */ + public $rdata; + + /** + * abstract definition - method to return a RR as a string; not to + * be confused with the __toString() magic method. + * + * @return string + * @access protected + * + */ + abstract protected function rrToString(); + + /** + * abstract definition - parses a RR from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + abstract protected function rrFromString(array $rdata); + + /** + * abstract definition - sets a Net_DNS2_RR from a Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + abstract protected function rrSet(Net_DNS2_Packet &$packet); + + /** + * abstract definition - returns a binary packet DNS RR object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed string or + * null on failure + * @access protected + * + */ + abstract protected function rrGet(Net_DNS2_Packet &$packet); + + /** + * Constructor - builds a new Net_DNS2_RR object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet or null to create + * an empty object + * @param array $rr an array with RR parse values or null to + * create an empty object + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct(Net_DNS2_Packet &$packet = null, array $rr = null) + { + if ( (!is_null($packet)) && (!is_null($rr)) ) { + + if ($this->set($packet, $rr) == false) { + + throw new Net_DNS2_Exception( + 'failed to generate resource record', + Net_DNS2_Lookups::E_RR_INVALID + ); + } + } else { + + $class = Net_DNS2_Lookups::$rr_types_class_to_id[get_class($this)]; + if (isset($class)) { + + $this->type = Net_DNS2_Lookups::$rr_types_by_id[$class]; + } + + $this->class = 'IN'; + $this->ttl = 86400; + } + } + + /** + * magic __toString() method to return the Net_DNS2_RR object object as a string + * + * @return string + * @access public + * + */ + public function __toString() + { + return $this->name . '. ' . $this->ttl . ' ' . $this->class . + ' ' . $this->type . ' ' . $this->rrToString(); + } + + /** + * return a formatted string; if a string has spaces in it, then return + * it with double quotes around it, otherwise, return it as it was passed in. + * + * @param string $string the string to format + * + * @return string + * @access protected + * + */ + protected function formatString($string) + { + return '"' . str_replace('"', '\"', trim($string, '"')) . '"'; + } + + /** + * builds an array of strings from an array of chunks of text split by spaces + * + * @param array $chunks an array of chunks of text split by spaces + * + * @return array + * @access protected + * + */ + protected function buildString(array $chunks) + { + $data = array(); + $c = 0; + $in = false; + + foreach ($chunks as $r) { + + $r = trim($r); + if (strlen($r) == 0) { + continue; + } + + if ( ($r[0] == '"') + && ($r[strlen($r) - 1] == '"') + && ($r[strlen($r) - 2] != '\\') + ) { + + $data[$c] = $r; + ++$c; + $in = false; + + } else if ($r[0] == '"') { + + $data[$c] = $r; + $in = true; + + } else if ( ($r[strlen($r) - 1] == '"') + && ($r[strlen($r) - 2] != '\\') + ) { + + $data[$c] .= ' ' . $r; + ++$c; + $in = false; + + } else { + + if ($in == true) { + $data[$c] .= ' ' . $r; + } else { + $data[$c++] = $r; + } + } + } + + foreach ($data as $index => $string) { + + $data[$index] = str_replace('\"', '"', trim($string, '"')); + } + + return $data; + } + + /** + * builds a new Net_DNS2_RR object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet or null to create + * an empty object + * @param array $rr an array with RR parse values or null to + * create an empty object + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function set(Net_DNS2_Packet &$packet, array $rr) + { + $this->name = $rr['name']; + $this->type = Net_DNS2_Lookups::$rr_types_by_id[$rr['type']]; + + // + // for RR OPT (41), the class value includes the requestors UDP payload size, + // and not a class value + // + if ($this->type == 'OPT') { + $this->class = $rr['class']; + } else { + $this->class = Net_DNS2_Lookups::$classes_by_id[$rr['class']]; + } + + $this->ttl = $rr['ttl']; + $this->rdlength = $rr['rdlength']; + $this->rdata = substr($packet->rdata, $packet->offset, $rr['rdlength']); + + return $this->rrSet($packet); + } + + /** + * returns a binary packed DNS RR object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet used for + * compressing names + * + * @return string + * @throws Net_DNS2_Exception + * @access public + * + */ + public function get(Net_DNS2_Packet &$packet) + { + $data = ''; + $rdata = ''; + + // + // pack the name + // + $data = $packet->compress($this->name, $packet->offset); + + // + // pack the main values + // + if ($this->type == 'OPT') { + + // + // pre-build the TTL value + // + $this->preBuild(); + + // + // the class value is different for OPT types + // + $data .= pack( + 'nnN', + Net_DNS2_Lookups::$rr_types_by_name[$this->type], + $this->class, + $this->ttl + ); + } else { + + $data .= pack( + 'nnN', + Net_DNS2_Lookups::$rr_types_by_name[$this->type], + Net_DNS2_Lookups::$classes_by_name[$this->class], + $this->ttl + ); + } + + // + // increase the offset, and allow for the rdlength + // + $packet->offset += 10; + + // + // get the RR specific details + // + if ($this->rdlength != -1) { + + $rdata = $this->rrGet($packet); + } + + // + // add the RR + // + $data .= pack('n', strlen($rdata)) . $rdata; + + return $data; + } + + /** + * parses a binary packet, and returns the appropriate Net_DNS2_RR object, + * based on the RR type of the binary content. + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet used for + * decompressing names + * + * @return mixed returns a new Net_DNS2_RR_* object for + * the given RR + * @throws Net_DNS2_Exception + * @access public + * + */ + public static function parse(Net_DNS2_Packet &$packet) + { + $object = array(); + + // + // expand the name + // + $object['name'] = $packet->expand($packet, $packet->offset); + if (is_null($object['name'])) { + + throw new Net_DNS2_Exception( + 'failed to parse resource record: failed to expand name.', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + if ($packet->rdlength < ($packet->offset + 10)) { + + throw new Net_DNS2_Exception( + 'failed to parse resource record: packet too small.', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + // + // unpack the RR details + // + $object['type'] = ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + $object['class'] = ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + + $object['ttl'] = ord($packet->rdata[$packet->offset++]) << 24 | + ord($packet->rdata[$packet->offset++]) << 16 | + ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + + $object['rdlength'] = ord($packet->rdata[$packet->offset++]) << 8 | + ord($packet->rdata[$packet->offset++]); + + if ($packet->rdlength < ($packet->offset + $object['rdlength'])) { + return null; + } + + // + // lookup the class to use + // + $o = null; + $class = Net_DNS2_Lookups::$rr_types_id_to_class[$object['type']]; + + if (isset($class)) { + + $o = new $class($packet, $object); + if ($o) { + + $packet->offset += $object['rdlength']; + } + } else { + + throw new Net_DNS2_Exception( + 'un-implemented resource record type: ' . $object['type'], + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + return $o; + } + + /** + * cleans up some RR data + * + * @param string $data the text string to clean + * + * @return string returns the cleaned string + * + * @access public + * + */ + public function cleanString($data) + { + return strtolower(rtrim($data, '.')); + } + + /** + * parses a standard RR format lines, as defined by rfc1035 (kinda) + * + * In our implementation, the domain *must* be specified- format must be + * + * <name> [<ttl>] [<class>] <type> <rdata> + * or + * <name> [<class>] [<ttl>] <type> <rdata> + * + * name, title, class and type are parsed by this function, rdata is passed + * to the RR specific classes for parsing. + * + * @param string $line a standard DNS config line + * + * @return mixed returns a new Net_DNS2_RR_* object for the given RR + * @throws Net_DNS2_Exception + * @access public + * + */ + public static function fromString($line) + { + if (strlen($line) == 0) { + throw new Net_DNS2_Exception( + 'empty config line provided.', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + $name = ''; + $type = ''; + $class = 'IN'; + $ttl = 86400; + + // + // split the line by spaces + // + $values = preg_split('/[\s]+/', $line); + if (count($values) < 3) { + + throw new Net_DNS2_Exception( + 'failed to parse config: minimum of name, type and rdata required.', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + // + // assume the first value is the name + // + $name = trim(strtolower(array_shift($values)), '.'); + + // + // The next value is either a TTL, Class or Type + // + foreach ($values as $value) { + + switch($value) { + case is_numeric($value): + + $ttl = array_shift($values); + break; + + // + // PHP SUCKS! + // + case ($value === 0): + $ttl = array_shift($values); + break; + + case isset(Net_DNS2_Lookups::$classes_by_name[strtoupper($value)]): + + $class = strtoupper(array_shift($values)); + break; + + case isset(Net_DNS2_Lookups::$rr_types_by_name[strtoupper($value)]): + + $type = strtoupper(array_shift($values)); + break 2; + break; + default: + + throw new Net_DNS2_Exception( + 'invalid config line provided: unknown file: ' . $value, + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + } + + // + // lookup the class to use + // + $o = null; + $class_name = Net_DNS2_Lookups::$rr_types_id_to_class[ + Net_DNS2_Lookups::$rr_types_by_name[$type] + ]; + + if (isset($class_name)) { + + $o = new $class_name; + if (!is_null($o)) { + + // + // set the parsed values + // + $o->name = $name; + $o->class = $class; + $o->ttl = $ttl; + + // + // parse the rdata + // + if ($o->rrFromString($values) === false) { + + throw new Net_DNS2_Exception( + 'failed to parse rdata for config: ' . $line, + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + } else { + + throw new Net_DNS2_Exception( + 'failed to create new RR record for type: ' . $type, + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + } else { + + throw new Net_DNS2_Exception( + 'un-implemented resource record type: '. $type, + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + return $o; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/A.php b/include/pear/Net/DNS2/RR/A.php new file mode 100644 index 0000000000000000000000000000000000000000..f8a0554615bb2b5bdd80d171a347123633ba0646 --- /dev/null +++ b/include/pear/Net/DNS2/RR/A.php @@ -0,0 +1,156 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: A.php 113 2011-07-25 02:54:19Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * A Resource Record - RFC1035 section 3.4.1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ADDRESS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_A extends Net_DNS2_RR +{ + /* + * The IPv4 address in quad-dotted notation + */ + public $address; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->address; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $value = array_shift($rdata); + + if (Net_DNS2::isIPv4($value) == true) { + + $this->address = $value; + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $this->address = inet_ntop($this->rdata); + if ($this->address !== false) { + + return true; + } + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + $packet->offset += 4; + return inet_pton($this->address); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/AAAA.php b/include/pear/Net/DNS2/RR/AAAA.php new file mode 100644 index 0000000000000000000000000000000000000000..86c15155d6d3308bf29dd818da921ff1ab92a011 --- /dev/null +++ b/include/pear/Net/DNS2/RR/AAAA.php @@ -0,0 +1,177 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: AAAA.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * A Resource Record - RFC1035 section 3.4.1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * | | + * | | + * | ADDRESS | + * | | + * | (128 bit) | + * | | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_AAAA extends Net_DNS2_RR +{ + /* + * the IPv6 address in the preferred hexadecimal values of the eight + * 16-bit pieces + * per RFC1884 + * + */ + public $address; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->address; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // expand out compressed formats + // + $value = array_shift($rdata); + if (Net_DNS2::isIPv6($value) == true) { + + $this->address = $value; + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + // + // must be 8 x 16bit chunks, or 16 x 8bit + // + if ($this->rdlength == 16) { + + // + // PHP's inet_ntop returns IPv6 addresses in their compressed form, + // but we want to keep with the preferred standard, so we'll parse + // it manually. + // + $x = unpack('n8', $this->rdata); + if (count($x) == 8) { + + $this->address = vsprintf('%x:%x:%x:%x:%x:%x:%x:%x', $x); + return true; + } + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + $packet->offset += 16; + return inet_pton($this->address); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/AFSDB.php b/include/pear/Net/DNS2/RR/AFSDB.php new file mode 100644 index 0000000000000000000000000000000000000000..0117a9a09d9372fe3d59ed4268ff8fe9202351f4 --- /dev/null +++ b/include/pear/Net/DNS2/RR/AFSDB.php @@ -0,0 +1,174 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: AFSDB.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * AFSDB Resource Record - RFC1183 section 1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | SUBTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / HOSTNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_AFSDB extends Net_DNS2_RR +{ + /* + * The AFSDB sub type + */ + public $subtype; + + /* + * The AFSDB hostname + */ + public $hostname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->subtype . ' ' . $this->cleanString($this->hostname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->subtype = array_shift($rdata); + $this->hostname = $this->cleanString(array_shift($rdata)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the subtype + // + $x = unpack('nsubtype', $this->rdata); + + $this->subtype = $x['subtype']; + $offset = $packet->offset + 2; + + $this->hostname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->hostname) > 0) { + + $data = pack('n', $this->subtype); + $packet->offset += 2; + + $data .= $packet->compress($this->hostname, $packet->offset); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/ANY.php b/include/pear/Net/DNS2/RR/ANY.php new file mode 100644 index 0000000000000000000000000000000000000000..915324058ec575cbb18868216181b7345f2fdd5d --- /dev/null +++ b/include/pear/Net/DNS2/RR/ANY.php @@ -0,0 +1,129 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: ANY.php 47 2010-10-24 23:53:08Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * This is only used for generating an empty ANY RR. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_ANY extends Net_DNS2_RR +{ + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return ''; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + return true; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + return ''; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/APL.php b/include/pear/Net/DNS2/RR/APL.php new file mode 100644 index 0000000000000000000000000000000000000000..da323f86dd72d4c74739fbb01cbdbb048e29f5bb --- /dev/null +++ b/include/pear/Net/DNS2/RR/APL.php @@ -0,0 +1,343 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: APL.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.0.0 + * + */ + +/** + * APL Resource Record - RFC3123 + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | ADDRESSFAMILY | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | PREFIX | N | AFDLENGTH | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * / AFDPART / + * | | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_APL extends Net_DNS2_RR +{ + /* + * a list of all the address prefix list items + */ + public $apl_items = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = ''; + + foreach ($this->apl_items as $item) { + + if ($item['n'] == 1) { + + $out .= '!'; + } + + $out .= $item['address_family'] . ':' . + $item['afd_part'] . '/' . $item['prefix'] . ' '; + } + + return trim($out); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + foreach ($rdata as $item) { + + if (preg_match('/^(!?)([1|2])\:([^\/]*)\/([0-9]{1,3})$/', $item, $m)) { + + $i = array( + + 'address_family' => $m[2], + 'prefix' => $m[4], + 'n' => ($m[1] == '!') ? 1 : 0, + 'afd_part' => strtolower($m[3]) + ); + + $address = $this->_trimZeros( + $i['address_family'], $i['afd_part'] + ); + + $i['afd_length'] = count(explode('.', $address)); + + $this->apl_items[] = $i; + } + } + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = 0; + + while ($offset < $this->rdlength) { + + // + // unpack the family, prefix, negate and length values + // + $x = unpack( + 'naddress_family/Cprefix/Cextra', substr($this->rdata, $offset) + ); + + $item = array( + + 'address_family' => $x['address_family'], + 'prefix' => $x['prefix'], + 'n' => ($x['extra'] >> 7) & 0x1, + 'afd_length' => $x['extra'] & 0xf + ); + + switch($item['address_family']) { + + case 1: + $r = unpack( + 'C*', substr($this->rdata, $offset + 4, $item['afd_length']) + ); + if (count($r) < 4) { + + for ($c=count($r)+1; $c<4+1; $c++) { + + $r[$c] = 0; + } + } + + $item['afd_part'] = implode('.', $r); + + break; + case 2: + $r = unpack( + 'C*', substr($this->rdata, $offset + 4, $item['afd_length']) + ); + if (count($r) < 8) { + + for ($c=count($r)+1; $c<8+1; $c++) { + + $r[$c] = 0; + } + } + + $item['afd_part'] = sprintf( + '%x:%x:%x:%x:%x:%x:%x:%x', + $r[1], $r[2], $r[3], $r[4], $r[5], $r[6], $r[7], $r[8] + ); + + break; + default: + return false; + } + + $this->apl_items[] = $item; + + $offset += 4 + $item['afd_length']; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (count($this->apl_items) > 0) { + + $data = ''; + + foreach ($this->apl_items as $item) { + + // + // pack the address_family and prefix values + // + $data .= pack( + 'nCC', + $item['address_family'], + $item['prefix'], + ($item['n'] << 7) | $item['afd_length'] + ); + + switch($item['address_family']) { + case 1: + $address = explode( + '.', + $this->_trimZeros($item['address_family'], $item['afd_part']) + ); + + foreach ($address as $b) { + $data .= chr($b); + } + break; + case 2: + $address = explode( + ':', + $this->_trimZeros($item['address_family'], $item['afd_part']) + ); + + foreach ($address as $b) { + $data .= pack('H', $b); + } + break; + default: + return null; + } + } + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } + + /** + * returns an IP address with the right-hand zero's trimmed + * + * @param integer $family the IP address family from the rdata + * @param string $address the IP address + * + * @return string the trimmed IP addresss. + * + * @access private + * + */ + private function _trimZeros($family, $address) + { + $a = array(); + + switch($family) { + case 1: + $a = array_reverse(explode('.', $address)); + break; + case 2: + $a = array_reverse(explode(':', $address)); + break; + default: + return ''; + } + + foreach ($a as $value) { + + if ($value === '0') { + + array_shift($a); + } + } + + $out = ''; + + switch($family) { + case 1: + $out = implode('.', array_reverse($a)); + break; + case 2: + $out = implode(':', array_reverse($a)); + break; + default: + return ''; + } + + return $out; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/ATMA.php b/include/pear/Net/DNS2/RR/ATMA.php new file mode 100644 index 0000000000000000000000000000000000000000..d9e51b0e7ed9b08278bd8d4f16b0664e7c3c68bc --- /dev/null +++ b/include/pear/Net/DNS2/RR/ATMA.php @@ -0,0 +1,210 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: ATMA.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.1.0 + * + */ + +/** + * ATMA Resource Record + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | FORMAT | | + * | +--+--+--+--+--+--+--+--+ + * / ADDRESS / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_ATMA extends Net_DNS2_RR +{ + /* + * One octet that indicates the format of ADDRESS. The two possible values + * for FORMAT are value 0 indicating ATM End System Address (AESA) format + * and value 1 indicating E.164 format + */ + public $format; + + /* + * The IPv4 address in quad-dotted notation + */ + public $address; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->address; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $value = array_shift($rdata); + + if (ctype_xdigit($value) == true) { + + $this->format = 0; + $this->address = $value; + + } else if (is_numeric($value) == true) { + + $this->format = 1; + $this->address = $value; + + } else { + + return false; + } + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the format + // + $x = unpack('Cformat/N*address', $this->rdata); + + $this->format = $x['format']; + + if ($this->format == 0) { + + $a = unpack('@1/H*address', $this->rdata); + + $this->address = $a['address']; + + } else if ($this->format == 1) { + + $this->address = substr($this->rdata, 1, $this->rdlength - 1); + + } else { + + return false; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + $data = chr($this->format); + + if ($this->format == 0) { + + $data .= pack('H*', $this->address); + + } else if ($this->format == 1) { + + $data .= $this->address; + + } else { + + return null; + } + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/CAA.php b/include/pear/Net/DNS2/RR/CAA.php new file mode 100644 index 0000000000000000000000000000000000000000..81d3601b4eccaf79d26fab407c042adb4cf646a0 --- /dev/null +++ b/include/pear/Net/DNS2/RR/CAA.php @@ -0,0 +1,179 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2011, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: CAA.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.0 + * + */ + +/** + * CAA Resource Record - http://tools.ietf.org/html/draft-ietf-pkix-caa-03 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | FLAGS | TAG LENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / TAG / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / DATA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_CAA extends Net_DNS2_RR +{ + /* + * The critcal flag + */ + public $flags; + + /* + * The property identifier + */ + public $tag; + + /* + * The property value + */ + public $value; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->flags . ' ' . $this->tag . ' "' . + trim($this->cleanString($this->value), '"') . '"'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->flags = array_shift($rdata); + $this->tag = array_shift($rdata); + + $this->value = trim($this->cleanString(implode($rdata, ' ')), '"'); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the flags and tag length + // + $x = unpack('Cflags/Ctag_length', $this->rdata); + + $this->flags = $x['flags']; + $offset = 2; + + $this->tag = substr($this->rdata, $offset, $x['tag_length']); + $offset += $x['tag_length']; + + $this->value = substr($this->rdata, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->value) > 0) { + + $data = chr($this->flags); + $data .= chr(strlen($this->tag)) . $this->tag . $this->value; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +?> diff --git a/include/pear/Net/DNS2/RR/CDS.php b/include/pear/Net/DNS2/RR/CDS.php new file mode 100644 index 0000000000000000000000000000000000000000..c9a08adc25252bcd56735e25e48c227c30689401 --- /dev/null +++ b/include/pear/Net/DNS2/RR/CDS.php @@ -0,0 +1,77 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2011, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: CDS.php 130 2011-12-03 05:02:37Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.0 + * + */ + +/** + * The CDS RR is implemented exactly like the DS record, so + * for now we just extend the DS RR and use it. + * + * http://tools.ietf.org/html/draft-barwood-dnsop-ds-publish-02 + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_CDS extends Net_DNS2_RR_DS +{ +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/CERT.php b/include/pear/Net/DNS2/RR/CERT.php new file mode 100644 index 0000000000000000000000000000000000000000..7f3773e382b6bdb7d5645cd51628a53454e3668f --- /dev/null +++ b/include/pear/Net/DNS2/RR/CERT.php @@ -0,0 +1,292 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: CERT.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * CERT Resource Record - RFC4398 section 2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | format | key tag | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | algorithm | / + * +---------------+ certificate or CRL / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_CERT extends Net_DNS2_RR +{ + /* + * format's allowed for certificates + */ + const CERT_FORMAT_RES = 0; + const CERT_FORMAT_PKIX = 1; + const CERT_FORMAT_SPKI = 2; + const CERT_FORMAT_PGP = 3; + const CERT_FORMAT_IPKIX = 4; + const CERT_FORMAT_ISPKI = 5; + const CERT_FORMAT_IPGP = 6; + const CERT_FORMAT_ACPKIX = 7; + const CERT_FORMAT_IACPKIX = 8; + const CERT_FORMAT_URI = 253; + const CERT_FORMAT_OID = 254; + + public $cert_format_name_to_id = array(); + public $cert_format_id_to_name = array( + + self::CERT_FORMAT_RES => 'Reserved', + self::CERT_FORMAT_PKIX => 'PKIX', + self::CERT_FORMAT_SPKI => 'SPKI', + self::CERT_FORMAT_PGP => 'PGP', + self::CERT_FORMAT_IPKIX => 'IPKIX', + self::CERT_FORMAT_ISPKI => 'ISPKI', + self::CERT_FORMAT_IPGP => 'IPGP', + self::CERT_FORMAT_ACPKIX => 'ACPKIX', + self::CERT_FORMAT_IACPKIX => 'IACPKIX', + self::CERT_FORMAT_URI => 'URI', + self::CERT_FORMAT_OID => 'OID' + ); + + /* + * certificate format + */ + public $format; + + /* + * key tag + */ + public $keytag; + + /* + * The algorithm used for the CERt + */ + public $algorithm; + + /* + * certificate + */ + public $certificate; + + /** + * we have our own constructor so that we can load our certificate + * information for parsing. + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * @param array $rr a array with parsed RR values + * + * @return + * + */ + public function __construct(Net_DNS2_Packet &$packet = null, array $rr = null) + { + parent::__construct($packet, $rr); + + // + // load the lookup values + // + $this->cert_format_name_to_id = array_flip($this->cert_format_id_to_name); + } + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->format . ' ' . $this->keytag . ' ' . $this->algorithm . + ' ' . base64_encode($this->certificate); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // load and check the format; can be an int, or a mnemonic symbol + // + $this->format = array_shift($rdata); + if (!is_numeric($this->format)) { + + $mnemonic = strtoupper(trim($this->format)); + if (!isset($this->cert_format_name_to_id[$mnemonic])) { + + return false; + } + + $this->format = $this->cert_format_name_to_id[$mnemonic]; + } else { + + if (!isset($this->cert_format_id_to_name[$this->format])) { + + return false; + } + } + + $this->keytag = array_shift($rdata); + + // + // parse and check the algorithm; can be an int, or a mnemonic symbol + // + $this->algorithm = array_shift($rdata); + if (!is_numeric($this->algorithm)) { + + $mnemonic = strtoupper(trim($this->algorithm)); + if (!isset(Net_DNS2_Lookups::$algorithm_name_to_id[$mnemonic])) { + + return false; + } + + $this->algorithm = Net_DNS2_Lookups::$algorithm_name_to_id[ + $mnemonic + ]; + } else { + + if (!isset(Net_DNS2_Lookups::$algorithm_id_to_name[$this->algorithm])) { + return false; + } + } + + // + // parse and base64 decode the certificate + // + // certificates MUST be provided base64 encoded, if not, everything will + // be broken after this point, as we assume it's base64 encoded. + // + $this->certificate = base64_decode(implode(' ', $rdata)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the format, keytag and algorithm + // + $x = unpack('nformat/nkeytag/Calgorithm', $this->rdata); + + $this->format = $x['format']; + $this->keytag = $x['keytag']; + $this->algorithm = $x['algorithm']; + + // + // copy the certificate + // + $this->certificate = substr($this->rdata, 5, $this->rdlength - 5); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->certificate) > 0) { + + $data = pack('nnC', $this->format, $this->keytag, $this->algorithm) . + $this->certificate; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/CNAME.php b/include/pear/Net/DNS2/RR/CNAME.php new file mode 100644 index 0000000000000000000000000000000000000000..7241f584145dc63680e2856f2d298278ead0e28a --- /dev/null +++ b/include/pear/Net/DNS2/RR/CNAME.php @@ -0,0 +1,153 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: CNAME.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * CNAME Resource Record - RFC1035 section 3.3.1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / CNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_CNAME extends Net_DNS2_RR +{ + /* + * The canonical name + */ + public $cname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->cname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->cname = $this->cleanString(array_shift($rdata)); + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + $this->cname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->cname) > 0) { + + return $packet->compress($this->cname, $packet->offset); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/DHCID.php b/include/pear/Net/DNS2/RR/DHCID.php new file mode 100644 index 0000000000000000000000000000000000000000..521880b8cf05b24c1bcd8cb9c3744b32b230993d --- /dev/null +++ b/include/pear/Net/DNS2/RR/DHCID.php @@ -0,0 +1,207 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DHCID.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * DHCID Resource Record - RFC4701 section 3.1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID Type Code | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | Digest Type | / + * +--+--+--+--+--+--+--+--+ / + * / / + * / Digest / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_DHCID extends Net_DNS2_RR +{ + /* + * Identifier type + */ + public $id_type; + + /* + * Digest Type + */ + public $digest_type; + + /* + * The digest + */ + public $digest; + + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = pack('nC', $this->id_type, $this->digest_type); + $out .= base64_decode($this->digest); + + return base64_encode($out); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = base64_decode(array_shift($rdata)); + if (strlen($data) > 0) { + + // + // unpack the id type and digest type + // + $x = unpack('nid_type/Cdigest_type', $data); + + $this->id_type = $x['id_type']; + $this->digest_type = $x['digest_type']; + + // + // copy out the digest + // + $this->digest = base64_encode(substr($data, 3, strlen($data) - 3)); + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the id type and digest type + // + $x = unpack('nid_type/Cdigest_type', $this->rdata); + + $this->id_type = $x['id_type']; + $this->digest_type = $x['digest_type']; + + // + // copy out the digest + // + $this->digest = base64_encode( + substr($this->rdata, 3, $this->rdlength - 3) + ); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->digest) > 0) { + + $data = pack('nC', $this->id_type, $this->digest_type) . + base64_decode($this->digest); + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/DLV.php b/include/pear/Net/DNS2/RR/DLV.php new file mode 100644 index 0000000000000000000000000000000000000000..bb235c2024d2ffed3198793620936118b88c6bd1 --- /dev/null +++ b/include/pear/Net/DNS2/RR/DLV.php @@ -0,0 +1,75 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DLV.php 47 2010-10-24 23:53:08Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * The DLV RR is implemented exactly like the DS RR; so we just extend that + * class, and use all of it's methods + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_DLV extends Net_DNS2_RR_DS +{ +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/DNAME.php b/include/pear/Net/DNS2/RR/DNAME.php new file mode 100644 index 0000000000000000000000000000000000000000..f5c631d82565221a2630cd9754851f96b337d698 --- /dev/null +++ b/include/pear/Net/DNS2/RR/DNAME.php @@ -0,0 +1,153 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DNAME.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * DNAME Resource Record - RFC2672 section 3 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / DNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_DNAME extends Net_DNS2_RR +{ + /* + * The target name + */ + public $dname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->dname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->dname = $this->cleanString(array_shift($rdata)); + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + $this->dname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->dname) > 0) { + + return $packet->compress($this->dname, $packet->offset); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/DNSKEY.php b/include/pear/Net/DNS2/RR/DNSKEY.php new file mode 100644 index 0000000000000000000000000000000000000000..d1c08a1e264b5b5c534977c822b1f7cd678e239b --- /dev/null +++ b/include/pear/Net/DNS2/RR/DNSKEY.php @@ -0,0 +1,198 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DNSKEY.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * DNSKEY Resource Record - RFC4034 sction 2.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags | Protocol | Algorithm | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / / + * / Public Key / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_DNSKEY extends Net_DNS2_RR +{ + /* + * flags + */ + public $flags; + + /* + * protocol + */ + public $protocol; + + /* + * algorithm used + */ + public $algorithm; + + /* + * the public key + */ + public $key; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->flags . ' ' . $this->protocol . ' ' . + $this->algorithm . ' ' . $this->key; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->flags = array_shift($rdata); + $this->protocol = array_shift($rdata); + $this->algorithm = array_shift($rdata); + $this->key = implode(' ', $rdata); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the flags, protocol and algorithm + // + $x = unpack('nflags/Cprotocol/Calgorithm', $this->rdata); + + // + // TODO: right now we're just displaying what's in DNS; we really + // should be parsing bit 7 and bit 15 of the flags field, and store + // those separately. + // + // right now the DNSSEC implementation is really just for display, + // we don't validate or handle any of the keys + // + $this->flags = $x['flags']; + $this->protocol = $x['protocol']; + $this->algorithm = $x['algorithm']; + + $this->key = base64_encode(substr($this->rdata, 4)); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->key) > 0) { + + $data = pack('nCC', $this->flags, $this->protocol, $this->algorithm); + $data .= base64_decode($this->key); + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/DS.php b/include/pear/Net/DNS2/RR/DS.php new file mode 100644 index 0000000000000000000000000000000000000000..407828ed6e973c8c9320a7312d546fecb82930c5 --- /dev/null +++ b/include/pear/Net/DNS2/RR/DS.php @@ -0,0 +1,209 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: DS.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * DS Resource Record - RFC4034 sction 5.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Key Tag | Algorithm | Digest Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / / + * / Digest / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_DS extends Net_DNS2_RR +{ + /* + * key tag + */ + public $keytag; + + /* + * algorithm number + */ + public $algorithm; + + /* + * algorithm used to construct the digest + */ + public $digesttype; + + /* + * the digest data + */ + public $digest; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->keytag . ' ' . $this->algorithm . ' ' . + $this->digesttype . ' ' . $this->digest; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->keytag = array_shift($rdata); + $this->algorithm = array_shift($rdata); + $this->digesttype = array_shift($rdata); + $this->digest = implode('', $rdata); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the keytag, algorithm and digesttype + // + $x = unpack('nkeytag/Calgorithm/Cdigesttype', $this->rdata); + + $this->keytag = $x['keytag']; + $this->algorithm = $x['algorithm']; + $this->digesttype = $x['digesttype']; + + // + // figure out the digest size + // + $digest_size = 0; + if ($this->digesttype == 1) { + + $digest_size = 20; // SHA1 + + } else if ($this->digesttype == 2) { + + $digest_size = 32; // SHA256 + } + + // + // copy the digest + // + $x = unpack('H*', substr($this->rdata, 4, $digest_size)); + $this->digest = $x[1]; + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->digest) > 0) { + + $data = pack( + 'nCCH*', + $this->keytag, $this->algorithm, $this->digesttype, $this->digest + ); + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/EID.php b/include/pear/Net/DNS2/RR/EID.php new file mode 100644 index 0000000000000000000000000000000000000000..17cf8fc27cd7dfe9c0f009fa558d499b3b0d922c --- /dev/null +++ b/include/pear/Net/DNS2/RR/EID.php @@ -0,0 +1,130 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: EID.php 125 2011-12-03 00:19:49Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * EID Resource Record - undefined; the rdata is simply used as-is in it's + * binary format, so not process has to be done. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_EID extends Net_DNS2_RR +{ + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return ''; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + return true; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + return $this->rdata; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/HINFO.php b/include/pear/Net/DNS2/RR/HINFO.php new file mode 100644 index 0000000000000000000000000000000000000000..355ec90839cd1ac45052ea5c7637e97b896660d5 --- /dev/null +++ b/include/pear/Net/DNS2/RR/HINFO.php @@ -0,0 +1,175 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: HINFO.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * HINFO Resource Record - RFC1035 section 3.3.2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / CPU / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / OS / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_HINFO extends Net_DNS2_RR +{ + /* + * computer informatino + */ + public $cpu; + + /* + * operataing system + */ + public $os; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->formatString($this->cpu) . ' ' . + $this->formatString($this->os); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = $this->buildString($rdata); + if (count($data) == 2) { + + $this->cpu = $data[0]; + $this->os = $data[1]; + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + + $this->cpu = trim(Net_DNS2_Packet::label($packet, $offset), '"'); + $this->os = trim(Net_DNS2_Packet::label($packet, $offset), '"'); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->cpu) > 0) { + + $data = chr(strlen($this->cpu)) . $this->cpu; + $data .= chr(strlen($this->os)) . $this->os; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/HIP.php b/include/pear/Net/DNS2/RR/HIP.php new file mode 100644 index 0000000000000000000000000000000000000000..8c0b80be45b078d097011b77cd708b465c9d05a8 --- /dev/null +++ b/include/pear/Net/DNS2/RR/HIP.php @@ -0,0 +1,287 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: HIP.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.0.0 + * + */ + +/** + * HIP Resource Record - RFC5205 section 5 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | HIT length | PK algorithm | PK length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * ~ HIT ~ + * | | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | + * +-+-+-+-+-+-+-+-+-+-+-+ + + * | Public Key | + * ~ ~ + * | | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * ~ Rendezvous Servers ~ + * | | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_HIP extends Net_DNS2_RR +{ + /* + * The length of the HIT field + */ + public $hit_length; + + /* + * the public key cryptographic algorithm + */ + public $pk_algorithm; + + /* + * the length of the public key field + */ + public $pk_length; + + /* + * The HIT is stored as a binary value in network byte order. + */ + public $hit; + + /* + * The public key + */ + public $public_key; + + /* + * a list of rendezvous servers + */ + public $rendezvous_servers = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->pk_algorithm . ' ' . + $this->hit . ' ' . $this->public_key . ' '; + + foreach ($this->rendezvous_servers as $index => $server) { + + $out .= $server . '. '; + } + + return trim($out); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->pk_algorithm = array_shift($rdata); + $this->hit = strtoupper(array_shift($rdata)); + $this->public_key = array_shift($rdata); + + // + // anything left on the array, must be one or more rendezevous servers. add + // them and strip off the trailing dot + // + if (count($rdata) > 0) { + + $this->rendezvous_servers = preg_replace('/\.$/', '', $rdata); + } + + // + // store the lengths; + // + $this->hit_length = strlen(pack('H*', $this->hit)); + $this->pk_length = strlen(base64_decode($this->public_key)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the algorithm and length values + // + $x = unpack('Chit_length/Cpk_algorithm/npk_length', $this->rdata); + + $this->hit_length = $x['hit_length']; + $this->pk_algorithm = $x['pk_algorithm']; + $this->pk_length = $x['pk_length']; + + $offset = 4; + + // + // copy out the HIT value + // + $hit = unpack('H*', substr($this->rdata, $offset, $this->hit_length)); + + $this->hit = strtoupper($hit[1]); + $offset += $this->hit_length; + + // + // copy out the public key + // + $this->public_key = base64_encode( + substr($this->rdata, $offset, $this->pk_length) + ); + $offset += $this->pk_length; + + // + // copy out any possible rendezvous servers + // + $offset = $packet->offset + $offset; + + while ( ($offset - $packet->offset) < $this->rdlength) { + + $this->rendezvous_servers[] = Net_DNS2_Packet::expand( + $packet, $offset + ); + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if ( (strlen($this->hit) > 0) && (strlen($this->public_key) > 0) ) { + + // + // pack the length, algorithm and HIT values + // + $data = pack( + 'CCnH*', + $this->hit_length, + $this->pk_algorithm, + $this->pk_length, + $this->hit + ); + + // + // add the public key + // + $data .= base64_decode($this->public_key); + + // + // add the offset + // + $packet->offset += strlen($data); + + // + // add each rendezvous server + // + foreach ($this->rendezvous_servers as $index => $server) { + + $data .= $packet->compress($server, $packet->offset); + } + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/IPSECKEY.php b/include/pear/Net/DNS2/RR/IPSECKEY.php new file mode 100644 index 0000000000000000000000000000000000000000..76a4c7fb247c4d10a6b24b4c46132b5db7f70a23 --- /dev/null +++ b/include/pear/Net/DNS2/RR/IPSECKEY.php @@ -0,0 +1,386 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: IPSECKEY.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * IPSECKEY Resource Record - RFC4025 section 2.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | precedence | gateway type | algorithm | gateway | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------+ + + * ~ gateway ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | / + * / public key / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_IPSECKEY extends Net_DNS2_RR +{ + const GATEWAY_TYPE_NONE = 0; + const GATEWAY_TYPE_IPV4 = 1; + const GATEWAY_TYPE_IPV6 = 2; + const GATEWAY_TYPE_DOMAIN = 3; + + const ALGORITHM_NONE = 0; + const ALGORITHM_DSA = 1; + const ALGORITHM_RSA = 2; + + /* + * Precedence (used the same was as a preference field) + */ + public $precedence; + + /* + * Gateway type - specifies the format of the gataway information + * This can be either: + * + * 0 No Gateway + * 1 IPv4 address + * 2 IPV6 address + * 3 wire-encoded domain name (not compressed) + * + */ + public $gateway_type; + + /* + * The algorithm used + * + * This can be: + * + * 0 No key is present + * 1 DSA key is present + * 2 RSA key is present + * + */ + public $algorithm; + + /* + * The gatway information + */ + public $gateway; + + /* + * the public key + */ + public $key; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->precedence . ' ' . $this->gateway_type . ' ' . + $this->algorithm . ' '; + + switch($this->gateway_type) { + case self::GATEWAY_TYPE_NONE: + $out .= '. '; + break; + + case self::GATEWAY_TYPE_IPV4: + case self::GATEWAY_TYPE_IPV6: + $out .= $this->gateway . ' '; + break; + + case self::GATEWAY_TYPE_DOMAIN: + $out .= $this->gateway . '. '; + break; + } + + $out .= $this->key; + return $out; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // load the data + // + $precedence = array_shift($rdata); + $gateway_type = array_shift($rdata); + $algorithm = array_shift($rdata); + $gateway = strtolower(trim(array_shift($rdata))); + $key = array_shift($rdata); + + // + // validate it + // + switch($gateway_type) { + case self::GATEWAY_TYPE_NONE: + $gateway = ''; + break; + + case self::GATEWAY_TYPE_IPV4: + if (Net_DNS2::isIPv4($gateway) == false) { + return false; + } + break; + + case self::GATEWAY_TYPE_IPV6: + if (Net_DNS2::isIPv6($gateway) == false) { + return false; + } + break; + + case self::GATEWAY_TYPE_DOMAIN: + ; // do nothing + break; + + default: + return false; + } + + // + // check the algorithm and key + // + switch($algorithm) { + case self::ALGORITHM_NONE: + $key = ''; + break; + + case self::ALGORITHM_DSA: + case self::ALGORITHM_RSA: + ; // do nothing + break; + + default: + return false; + } + + // + // store the values + // + $this->precedence = $precedence; + $this->gateway_type = $gateway_type; + $this->algorithm = $algorithm; + $this->gateway = $gateway; + $this->key = $key; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse off the precedence, gateway type and algorithm + // + $x = unpack('Cprecedence/Cgateway_type/Calgorithm', $this->rdata); + + $this->precedence = $x['precedence']; + $this->gateway_type = $x['gateway_type']; + $this->algorithm = $x['algorithm']; + + $offset = 3; + + // + // extract the gatway based on the type + // + switch($this->gateway_type) { + case self::GATEWAY_TYPE_NONE: + $this->gateway = ''; + break; + + case self::GATEWAY_TYPE_IPV4: + $this->gateway = inet_ntop(substr($this->rdata, $offset, 4)); + $offset += 4; + break; + + case self::GATEWAY_TYPE_IPV6: + $ip = unpack('n8', substr($this->rdata, $offset, 16)); + if (count($ip) == 8) { + + $this->gateway = vsprintf('%x:%x:%x:%x:%x:%x:%x:%x', $ip); + $offset += 16; + } else { + + return false; + } + break; + + case self::GATEWAY_TYPE_DOMAIN: + + $doffset = $offset + $packet->offset; + $this->gateway = Net_DNS2_Packet::expand($packet, $doffset); + $offset = ($doffset - $packet->offset); + break; + + default: + return false; + } + + // + // extract the key + // + switch($this->algorithm) { + case self::ALGORITHM_NONE: + $this->key = ''; + break; + + case self::ALGORITHM_DSA: + case self::ALGORITHM_RSA: + $this->key = base64_encode(substr($this->rdata, $offset)); + break; + + default: + return false; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + // + // pack the precedence, gateway type and algorithm + // + $data = pack( + 'CCC', $this->precedence, $this->gateway_type, $this->algorithm + ); + + // + // add the gateway based on the type + // + switch($this->gateway_type) { + case self::GATEWAY_TYPE_NONE: + ; // add nothing + break; + + case self::GATEWAY_TYPE_IPV4: + case self::GATEWAY_TYPE_IPV6: + $data .= inet_pton($this->gateway); + break; + + case self::GATEWAY_TYPE_DOMAIN: + $data .= chr(strlen($this->gateway)) . $this->gateway; + break; + + default: + return null; + } + + // + // add the key if there's one specified + // + switch($this->algorithm) { + case self::ALGORITHM_NONE: + ; // add nothing + break; + + case self::ALGORITHM_DSA: + case self::ALGORITHM_RSA: + $data .= base64_decode($this->key); + break; + + default: + return null; + } + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/ISDN.php b/include/pear/Net/DNS2/RR/ISDN.php new file mode 100644 index 0000000000000000000000000000000000000000..94db77a68deeff5b0b68cccb27e74b8bda77cc08 --- /dev/null +++ b/include/pear/Net/DNS2/RR/ISDN.php @@ -0,0 +1,190 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: ISDN.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * ISDN Resource Record - RFC1183 section 3.2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / ISDN-address / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / SA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_ISDN extends Net_DNS2_RR +{ + /* + * ISDN Number + */ + public $isdnaddress; + + /* + * Sub-Address + */ + public $sa; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->formatString($this->isdnaddress) . ' ' . + $this->formatString($this->sa); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = $this->buildString($rdata); + if (count($data) >= 1) { + + $this->isdnaddress = $data[0]; + if (isset($data[1])) { + + $this->sa = $data[1]; + } + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $this->isdnaddress = Net_DNS2_Packet::label($packet, $packet->offset); + + // + // look for a SA (sub address) - it's optional + // + if ( (strlen($this->isdnaddress) + 1) < $this->rdlength) { + + $this->sa = Net_DNS2_Packet::label($packet, $packet->offset); + } else { + + $this->sa = ''; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->isdnaddress) > 0) { + + $data = chr(strlen($this->isdnaddress)) . $this->isdnaddress; + if (!empty($this->sa)) { + + $data .= chr(strlen($this->sa)); + $data .= $this->sa; + } + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/KEY.php b/include/pear/Net/DNS2/RR/KEY.php new file mode 100644 index 0000000000000000000000000000000000000000..6bcb0f596cbe6700cf12646c8575792fb03da634 --- /dev/null +++ b/include/pear/Net/DNS2/RR/KEY.php @@ -0,0 +1,85 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: KEY.php 47 2010-10-24 23:53:08Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * the KEY RR is implemented the same as the DNSKEY RR, the only difference + * is how the flags data is parsed. + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | A/C | Z | XT| Z | Z | NAMTYP| Z | Z | Z | Z | SIG | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * DNSKEY only uses bits 7 and 15 + * + * We're not doing anything with these flags right now, so duplicating the + * class like this is fine. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_KEY extends Net_DNS2_RR_DNSKEY +{ +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/KX.php b/include/pear/Net/DNS2/RR/KX.php new file mode 100644 index 0000000000000000000000000000000000000000..0218c390c2fce8f3f0a93526d76623609875250f --- /dev/null +++ b/include/pear/Net/DNS2/RR/KX.php @@ -0,0 +1,179 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: KX.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * KX Resource Record - RFC2230 section 3.1 + * + * This class is almost identical to MX, except that the the exchanger + * domain is not compressed, it's added as a label + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PREFERENCE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / EXCHANGER / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_KX extends Net_DNS2_RR +{ + /* + * the preference for this mail exchanger + */ + public $preference; + + /* + * the hostname of the mail exchanger + */ + public $exchange; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->cleanString($this->exchange) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->exchange = $this->cleanString(array_shift($rdata)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse the preference + // + $x = unpack('npreference', $this->rdata); + $this->preference = $x['preference']; + + // + // get the exchange entry server) + // + $offset = $packet->offset + 2; + $this->exchange = Net_DNS2_Packet::label($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->exchange) > 0) { + + $data = pack('nC', $this->preference, strlen($this->exchange)) . + $this->exchange; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/L32.php b/include/pear/Net/DNS2/RR/L32.php new file mode 100644 index 0000000000000000000000000000000000000000..b26d7cb9f0c6ba81f6f4ee717cda542a23509fac --- /dev/null +++ b/include/pear/Net/DNS2/RR/L32.php @@ -0,0 +1,180 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2013, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2013 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: L32.php 207 2013-06-13 01:19:55Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.3.1 + * + */ + +/** + * L32 Resource Record - RFC6742 section 2.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preference | Locator32 (16 MSBs) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Locator32 (16 LSBs) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_L32 extends Net_DNS2_RR +{ + /* + * The preference + */ + public $preference; + + /* + * The locator32 field + */ + public $locator32; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->locator32; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->locator32 = array_shift($rdata); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the values + // + $x = unpack('npreference/C4locator', $this->rdata); + + $this->preference = $x['preference']; + + // + // build the locator value + // + $this->locator32 = $x['locator1'] . '.' . $x['locator2'] . '.' . + $x['locator3'] . '.' . $x['locator4']; + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->locator32) > 0) { + + // + // break out the locator value + // + $n = explode('.', $this->locator32); + + // + // pack the data + // + return pack('nC4', $this->preference, $n[0], $n[1], $n[2], $n[3]); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/L64.php b/include/pear/Net/DNS2/RR/L64.php new file mode 100644 index 0000000000000000000000000000000000000000..900b433457b39987b97622d667297c6216f0e4eb --- /dev/null +++ b/include/pear/Net/DNS2/RR/L64.php @@ -0,0 +1,187 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2013, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2013 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: L64.php 208 2013-06-13 01:22:36Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.3.1 + * + */ + +/** + * L64 Resource Record - RFC6742 section 2.3 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preference | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | Locator64 | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_L64 extends Net_DNS2_RR +{ + /* + * The preference + */ + public $preference; + + /* + * The locator64 field + */ + public $locator64; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->locator64; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->locator64 = array_shift($rdata); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the values + // + $x = unpack('npreference/n4locator', $this->rdata); + + $this->preference = $x['preference']; + + // + // build the locator64 + // + $this->locator64 = dechex($x['locator1']) . ':' . + dechex($x['locator2']) . ':' . + dechex($x['locator3']) . ':' . + dechex($x['locator4']); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->locator64) > 0) { + + // + // break out the locator64 + // + $n = explode(':', $this->locator64); + + // + // pack the data + // + return pack( + 'n5', $this->preference, hexdec($n[0]), hexdec($n[1]), + hexdec($n[2]), hexdec($n[3]) + ); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/LOC.php b/include/pear/Net/DNS2/RR/LOC.php new file mode 100644 index 0000000000000000000000000000000000000000..7afd2fabb8d77c39422a5a3c4adb390e74ba8339 --- /dev/null +++ b/include/pear/Net/DNS2/RR/LOC.php @@ -0,0 +1,440 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: LOC.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * LOC Resource Record - RFC1876 section 2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | VERSION | SIZE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | HORIZ PRE | VERT PRE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | LATITUDE | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | LONGITUDE | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ALTITUDE | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_LOC extends Net_DNS2_RR +{ + /* + * the LOC version- should only ever be 0 + */ + public $version; + + /* + * The diameter of a sphere enclosing the described entity + */ + public $size; + + /* + * The horizontal precision of the data + */ + public $horiz_pre; + + /* + * The vertical precision of the data + */ + public $vert_pre; + + /* + * The latitude - stored in decimal degrees + */ + public $latitude; + + /* + * The longitude - stored in decimal degrees + */ + public $longitude; + + /* + * The altitude - stored in decimal + */ + public $altitude; + + /* + * used for quick power-of-ten lookups + */ + private $_powerOfTen = array(1, 10, 100, 1000, 10000, 100000, + 1000000,10000000,100000000,1000000000); + + /* + * some conversion values + */ + const CONV_SEC = 1000; + const CONV_MIN = 60000; + const CONV_DEG = 3600000; + + const REFERENCE_ALT = 10000000; + const REFERENCE_LATLON = 2147483648; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + if ($this->version == 0) { + + return $this->_d2Dms($this->latitude, 'LAT') . ' ' . + $this->_d2Dms($this->longitude, 'LNG') . ' ' . + sprintf('%.2fm', $this->altitude) . ' ' . + sprintf('%.2fm', $this->size) . ' ' . + sprintf('%.2fm', $this->horiz_pre) . ' ' . + sprintf('%.2fm', $this->vert_pre); + } + + return ''; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // format as defined by RFC1876 section 3 + // + // d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]] {"E"|"W"} alt["m"] + // [siz["m"] [hp["m"] [vp["m"]]]] + // + $res = preg_match( + '/^(\d+) \s+((\d+) \s+)?(([\d.]+) \s+)?(N|S) \s+(\d+) ' . + '\s+((\d+) \s+)?(([\d.]+) \s+)?(E|W) \s+(-?[\d.]+) m?(\s+ ' . + '([\d.]+) m?)?(\s+ ([\d.]+) m?)?(\s+ ([\d.]+) m?)?/ix', + implode(' ', $rdata), $x + ); + + if ($res) { + + // + // latitude + // + $latdeg = $x[1]; + $latmin = (isset($x[3])) ? $x[3] : 0; + $latsec = (isset($x[5])) ? $x[5] : 0; + $lathem = strtoupper($x[6]); + + $this->latitude = $this->_dms2d($latdeg, $latmin, $latsec, $lathem); + + // + // longitude + // + $londeg = $x[7]; + $lonmin = (isset($x[9])) ? $x[9] : 0; + $lonsec = (isset($x[11])) ? $x[11] : 0; + $lonhem = strtoupper($x[12]); + + $this->longitude = $this->_dms2d($londeg, $lonmin, $lonsec, $lonhem); + + // + // the rest of teh values + // + $version = 0; + + $this->size = (isset($x[15])) ? $x[15] : 1; + $this->horiz_pre = ((isset($x[17])) ? $x[17] : 10000); + $this->vert_pre = ((isset($x[19])) ? $x[19] : 10); + $this->altitude = $x[13]; + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack all the values + // + $x = unpack( + 'Cver/Csize/Choriz_pre/Cvert_pre/Nlatitude/Nlongitude/Naltitude', + $this->rdata + ); + + // + // version must be 0 per RFC 1876 section 2 + // + $this->version = $x['ver']; + if ($this->version == 0) { + + $this->size = $this->_precsizeNtoA($x['size']); + $this->horiz_pre = $this->_precsizeNtoA($x['horiz_pre']); + $this->vert_pre = $this->_precsizeNtoA($x['vert_pre']); + + // + // convert the latitude and longitude to degress in decimal + // + if ($x['latitude'] < 0) { + + $this->latitude = ($x['latitude'] + + self::REFERENCE_LATLON) / self::CONV_DEG; + } else { + + $this->latitude = ($x['latitude'] - + self::REFERENCE_LATLON) / self::CONV_DEG; + } + + if ($x['longitude'] < 0) { + + $this->longitude = ($x['longitude'] + + self::REFERENCE_LATLON) / self::CONV_DEG; + } else { + + $this->longitude = ($x['longitude'] - + self::REFERENCE_LATLON) / self::CONV_DEG; + } + + // + // convert down the altitude + // + $this->altitude = ($x['altitude'] - self::REFERENCE_ALT) / 100; + + return true; + + } else { + + return false; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if ($this->version == 0) { + + $lat = 0; + $lng = 0; + + if ($this->latitude < 0) { + + $lat = ($this->latitude * self::CONV_DEG) - self::REFERENCE_LATLON; + } else { + + $lat = ($this->latitude * self::CONV_DEG) + self::REFERENCE_LATLON; + } + + if ($this->longitude < 0) { + + $lng = ($this->longitude * self::CONV_DEG) - self::REFERENCE_LATLON; + } else { + + $lng = ($this->longitude * self::CONV_DEG) + self::REFERENCE_LATLON; + } + + $packet->offset += 16; + + return pack( + 'CCCCNNN', + $this->version, + $this->_precsizeAtoN($this->size), + $this->_precsizeAtoN($this->horiz_pre), + $this->_precsizeAtoN($this->vert_pre), + $lat, $lng, + ($this->altitude * 100) + self::REFERENCE_ALT + ); + } + + return null; + } + + /** + * takes an XeY precision/size value, returns a string representation. + * shamlessly stolen from RFC1876 Appendix A + * + * @param integer $prec the value to convert + * + * @return string + * @access private + * + */ + private function _precsizeNtoA($prec) + { + $mantissa = (($prec >> 4) & 0x0f) % 10; + $exponent = (($prec >> 0) & 0x0f) % 10; + + return $mantissa * $this->_powerOfTen[$exponent]; + } + + /** + * converts ascii size/precision X * 10**Y(cm) to 0xXY. + * shamlessly stolen from RFC1876 Appendix A + * + * @param string $prec the value to convert + * + * @return integer + * @access private + * + */ + private function _precsizeAtoN($prec) + { + $exponent = 0; + while ($prec >= 10) { + + $prec /= 10; + ++$exponent; + } + + return ($prec << 4) | ($exponent & 0x0f); + } + + /** + * convert lat/lng in deg/min/sec/hem to decimal value + * + * @param integer $deg the degree value + * @param integer $min the minutes value + * @param integer $sec the seconds value + * @param string $hem the hemisphere (N/E/S/W) + * + * @return float the decinmal value + * @access private + * + */ + private function _dms2d($deg, $min, $sec, $hem) + { + $deg = $deg - 0; + $min = $min - 0; + + $sign = ($hem == 'W' || $hem == 'S') ? -1 : 1; + return ((($sec/60+$min)/60)+$deg) * $sign; + } + + /** + * convert lat/lng in decimal to deg/min/sec/hem + * + * @param float $data the decimal value + * @param string $latlng either LAT or LNG so we can determine the HEM value + * + * @return string + * @access private + * + */ + private function _d2Dms($data, $latlng) + { + $deg = 0; + $min = 0; + $sec = 0; + $msec = 0; + $hem = ''; + + if ($latlng == 'LAT') { + $hem = ($data > 0) ? 'N' : 'S'; + } else { + $hem = ($data > 0) ? 'E' : 'W'; + } + + $data = abs($data); + + $deg = (int)$data; + $min = (int)(($data - $deg) * 60); + $sec = (int)(((($data - $deg) * 60) - $min) * 60); + $msec = round((((((($data - $deg) * 60) - $min) * 60) - $sec) * 1000)); + + return sprintf('%d %02d %02d.%03d %s', $deg, $min, $sec, round($msec), $hem); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/LP.php b/include/pear/Net/DNS2/RR/LP.php new file mode 100644 index 0000000000000000000000000000000000000000..7c5356c09bcde506d15de72b2916f48647b267d2 --- /dev/null +++ b/include/pear/Net/DNS2/RR/LP.php @@ -0,0 +1,177 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2013, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2013 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: LP.php 207 2013-06-13 01:19:55Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.3.1 + * + */ + +/** + * LP Resource Record - RFC6742 section 2.4 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preference | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / + * / / + * / FQDN / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_LP extends Net_DNS2_RR +{ + /* + * The preference + */ + public $preference; + + /* + * The fdqn field + */ + public $fqdn; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->fqdn . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->fqdn = trim(array_shift($rdata), '.'); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse the preference + // + $x = unpack('npreference', $this->rdata); + $this->preference = $x['preference']; + $offset = $packet->offset + 2; + + // + // get the hostname + // + $this->fqdn = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->fqdn) > 0) { + + $data = pack('n', $this->preference); + $packet->offset += 2; + + $data .= $packet->compress($this->fqdn, $packet->offset); + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/MX.php b/include/pear/Net/DNS2/RR/MX.php new file mode 100644 index 0000000000000000000000000000000000000000..d75af38dff2974461aa4d000909b4241029bdc56 --- /dev/null +++ b/include/pear/Net/DNS2/RR/MX.php @@ -0,0 +1,175 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: MX.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * MX Resource Record - RFC1035 section 3.3.9 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PREFERENCE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / EXCHANGE / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_MX extends Net_DNS2_RR +{ + /* + * the preference for this mail exchanger + */ + public $preference; + + /* + * the hostname of the mail exchanger + */ + public $exchange; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->cleanString($this->exchange) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->exchange = $this->cleanString(array_shift($rdata)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse the preference + // + $x = unpack('npreference', $this->rdata); + $this->preference = $x['preference']; + + // + // get the exchange entry server) + // + $offset = $packet->offset + 2; + $this->exchange = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->exchange) > 0) { + + $data = pack('n', $this->preference); + $packet->offset += 2; + + $data .= $packet->compress($this->exchange, $packet->offset); + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NAPTR.php b/include/pear/Net/DNS2/RR/NAPTR.php new file mode 100644 index 0000000000000000000000000000000000000000..d05d9da55c3b8686399a55a071334e6f8bc72238 --- /dev/null +++ b/include/pear/Net/DNS2/RR/NAPTR.php @@ -0,0 +1,231 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NAPTR.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NAPTR Resource Record - RFC2915 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ORDER | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PREFERENCE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / FLAGS / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / SERVICES / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / REGEXP / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / REPLACEMENT / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NAPTR extends Net_DNS2_RR +{ + /* + * the order in which the NAPTR records MUST be processed + */ + public $order; + + /* + * specifies the order in which NAPTR records with equal "order" + * values SHOULD be processed + */ + public $preference; + + /* + * rewrite flags + */ + public $flags; + + /* + * Specifies the service(s) available down this rewrite path + */ + public $services; + + /* + * regular expression + */ + public $regexp; + + /* + * The next NAME to query for NAPTR, SRV, or address records + * depending on the value of the flags field + */ + public $replacement; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->order . ' ' . $this->preference . ' ' . + $this->formatString($this->flags) . ' ' . + $this->formatString($this->services) . ' ' . + $this->formatString($this->regexp) . ' ' . + $this->cleanString($this->replacement) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->order = array_shift($rdata); + $this->preference = array_shift($rdata); + + $data = $this->buildString($rdata); + if (count($data) == 4) { + + $this->flags = $data[0]; + $this->services = $data[1]; + $this->regexp = $data[2]; + $this->replacement = $this->cleanString($data[3]); + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the order and preference + // + $x = unpack('norder/npreference', $this->rdata); + + $this->order = $x['order']; + $this->preference = $x['preference']; + + $offset = $packet->offset + 4; + + $this->flags = Net_DNS2_Packet::label($packet, $offset); + $this->services = Net_DNS2_Packet::label($packet, $offset); + $this->regexp = Net_DNS2_Packet::label($packet, $offset); + + $this->replacement = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if ( (isset($this->order)) && (strlen($this->services) > 0) ) { + + $data = pack('nn', $this->order, $this->preference); + + $data .= chr(strlen($this->flags)) . $this->flags; + $data .= chr(strlen($this->services)) . $this->services; + $data .= chr(strlen($this->regexp)) . $this->regexp; + + $packet->offset += strlen($data); + + $data .= $packet->compress($this->replacement, $packet->offset); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NID.php b/include/pear/Net/DNS2/RR/NID.php new file mode 100644 index 0000000000000000000000000000000000000000..ed4463de5b4f8cd4ffd30edf5e6b96f644abbe7f --- /dev/null +++ b/include/pear/Net/DNS2/RR/NID.php @@ -0,0 +1,187 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2013, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2013 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NID.php 208 2013-06-13 01:22:36Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.3.1 + * + */ + +/** + * NID Resource Record - RFC6742 section 2.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preference | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | NodeID | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NID extends Net_DNS2_RR +{ + /* + * The preference + */ + public $preference; + + /* + * The node ID field + */ + public $nodeid; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->nodeid; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = array_shift($rdata); + $this->nodeid = array_shift($rdata); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the values + // + $x = unpack('npreference/n4nodeid', $this->rdata); + + $this->preference = $x['preference']; + + // + // build the node id + // + $this->nodeid = dechex($x['nodeid1']) . ':' . + dechex($x['nodeid2']) . ':' . + dechex($x['nodeid3']) . ':' . + dechex($x['nodeid4']); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->nodeid) > 0) { + + // + // break out the node id + // + $n = explode(':', $this->nodeid); + + // + // pack the data + // + return pack( + 'n5', $this->preference, hexdec($n[0]), hexdec($n[1]), + hexdec($n[2]), hexdec($n[3]) + ); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NIMLOC.php b/include/pear/Net/DNS2/RR/NIMLOC.php new file mode 100644 index 0000000000000000000000000000000000000000..70c421975f96d8eb07bfbd8276ccbb50ea7f1b17 --- /dev/null +++ b/include/pear/Net/DNS2/RR/NIMLOC.php @@ -0,0 +1,130 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NIMLOC.php 125 2011-12-03 00:19:49Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NIMLOCK Resource Record - undefined; the rdata is simply used as-is in it's + * binary format, so not process has to be done. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NIMLOCK extends Net_DNS2_RR +{ + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return ''; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + return true; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + return $this->rdata; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NS.php b/include/pear/Net/DNS2/RR/NS.php new file mode 100644 index 0000000000000000000000000000000000000000..3d006872a0760fdd13ac157ff661d38ccb3e279c --- /dev/null +++ b/include/pear/Net/DNS2/RR/NS.php @@ -0,0 +1,153 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NS.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NS Resource Record - RFC1035 section 3.3.11 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / NSDNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NS extends Net_DNS2_RR +{ + /* + * the hostname of the DNS server + */ + public $nsdname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->nsdname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->nsdname = $this->cleanString(array_shift($rdata)); + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + $this->nsdname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->nsdname) > 0) { + + return $packet->compress($this->nsdname, $packet->offset); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NSAP.php b/include/pear/Net/DNS2/RR/NSAP.php new file mode 100644 index 0000000000000000000000000000000000000000..4413ebcec50d674298ad9e18fac0f85ff97f1406 --- /dev/null +++ b/include/pear/Net/DNS2/RR/NSAP.php @@ -0,0 +1,262 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NSAP.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NSAP Resource Record - RFC1706 + * + * |--------------| + * | <-- IDP --> | + * |--------------|-------------------------------------| + * | AFI | IDI | <-- DSP --> | + * |-----|--------|-------------------------------------| + * | 47 | 0005 | DFI | AA |Rsvd | RD |Area | ID |Sel | + * |-----|--------|-----|----|-----|----|-----|----|----| + * octets | 1 | 2 | 1 | 3 | 2 | 2 | 2 | 6 | 1 | + * |-----|--------|-----|----|-----|----|-----|----|----| + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NSAP extends Net_DNS2_RR +{ + public $afi; + public $idi; + public $dfi; + public $aa; + public $rsvd; + public $rd; + public $area; + public $id; + public $sel; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->afi) . '.' . + $this->cleanString($this->idi) . '.' . + $this->cleanString($this->dfi) . '.' . + $this->cleanString($this->aa) . '.' . + $this->cleanString($this->rsvd) . '.' . + $this->cleanString($this->rd) . '.' . + $this->cleanString($this->area) . '.' . + $this->cleanString($this->id) . '.' . + $this->sel; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = strtolower(trim(array_shift($rdata))); + + // + // there is no real standard for format, so we can't rely on the fact that + // the value will come in with periods separating the values- so strip + // them out if they're included, and parse without them. + // + $data = str_replace(array('.', '0x'), '', $data); + + // + // unpack it as ascii characters + // + $x = unpack('A2afi/A4idi/A2dfi/A6aa/A4rsvd/A4rd/A4area/A12id/A2sel', $data); + + // + // make sure the afi value is 47 + // + if ($x['afi'] == 47) { + + $this->afi = '0x' . $x['afi']; + $this->idi = $x['idi']; + $this->dfi = $x['dfi']; + $this->aa = $x['aa']; + $this->rsvd = $x['rsvd']; + $this->rd = $x['rd']; + $this->area = $x['area']; + $this->id = $x['id']; + $this->sel = $x['sel']; + + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength == 20) { + + // + // get the AFI value + // + $this->afi = dechex(ord($this->rdata[0])); + + // + // we only support AFI 47- there arent' any others defined. + // + if ($this->afi == 47) { + + // + // unpack the rest of the values + // + $x = unpack( + 'Cafi/nidi/Cdfi/C3aa/nrsvd/nrd/narea/Nidh/nidl/Csel', + $this->rdata + ); + + $this->afi = sprintf('0x%02x', $x['afi']); + $this->idi = sprintf('%04x', $x['idi']); + $this->dfi = sprintf('%02x', $x['dfi']); + $this->aa = sprintf( + '%06x', $x['aa1'] << 16 | $x['aa2'] << 8 | $x['aa3'] + ); + $this->rsvd = sprintf('%04x', $x['rsvd']); + $this->rd = sprintf('%04x', $x['rd']); + $this->area = sprintf('%04x', $x['area']); + $this->id = sprintf('%08x', $x['idh']) . + sprintf('%04x', $x['idl']); + $this->sel = sprintf('%02x', $x['sel']); + + return true; + } + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if ($this->afi == 0x47) { + + // + // build the aa field + // + $aa = unpack('A2x/A2y/A2z', $this->aa); + + // + // build the id field + // + $id = unpack('A8a/A4b', $this->id); + + // + $data = pack( + 'CnCCCCnnnNnC', + hexdec($this->afi), + hexdec($this->idi), + hexdec($this->dfi), + hexdec($aa['x']), + hexdec($aa['y']), + hexdec($aa['z']), + hexdec($this->rsvd), + hexdec($this->rd), + hexdec($this->area), + hexdec($id['a']), + hexdec($id['b']), + hexdec($this->sel) + ); + + if (strlen($data) == 20) { + + $packet->offset += 20; + return $data; + } + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NSEC.php b/include/pear/Net/DNS2/RR/NSEC.php new file mode 100644 index 0000000000000000000000000000000000000000..d87d525423713664aae43371ed35dcbc755fdd73 --- /dev/null +++ b/include/pear/Net/DNS2/RR/NSEC.php @@ -0,0 +1,184 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NSEC.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NSEC Resource Record - RFC3845 section 2.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / Next Domain Name / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / List of Type Bit Map(s) / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NSEC extends Net_DNS2_RR +{ + /* + * The next owner name + */ + public $next_domain_name; + + /* + * identifies the RRset types that exist at the NSEC RR's owner name. + */ + public $type_bit_maps = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $data = $this->cleanString($this->next_domain_name) . '.'; + + foreach ($this->type_bit_maps as $rr) { + + $data .= ' ' . $rr; + } + + return $data; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->next_domain_name = $this->cleanString(array_shift($rdata)); + $this->type_bit_maps = $rdata; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // expand the next domain name + // + $offset = $packet->offset; + $this->next_domain_name = Net_DNS2_Packet::expand($packet, $offset); + + // + // parse out the RR's from the bitmap + // + $this->type_bit_maps = Net_DNS2_BitMap::bitMapToArray( + substr($this->rdata, $offset - $packet->offset) + ); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->next_domain_name) > 0) { + + $data = $packet->compress($this->next_domain_name, $packet->offset); + $bitmap = Net_DNS2_BitMap::arrayToBitMap($this->type_bit_maps); + + $packet->offset += strlen($bitmap); + + return $data . $bitmap; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NSEC3.php b/include/pear/Net/DNS2/RR/NSEC3.php new file mode 100644 index 0000000000000000000000000000000000000000..71dc028cb5150eceaafcbf4e9b5bd215f9aad833 --- /dev/null +++ b/include/pear/Net/DNS2/RR/NSEC3.php @@ -0,0 +1,310 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NSEC3.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NSEC3 Resource Record - RFC5155 section 3.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Hash Alg. | Flags | Iterations | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Salt Length | Salt / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Hash Length | Next Hashed Owner Name / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / Type Bit Maps / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NSEC3 extends Net_DNS2_RR +{ + /* + * Algorithm to use + */ + public $algorithm; + + /* + * flags + */ + public $flags; + + /* + * defines the number of additional times the hash is performed. + */ + public $iterations; + + /* + * the length of the salt- not displayed + */ + public $salt_length; + + /* + * the salt + */ + public $salt; + + /* + * the length of the hash value + */ + public $hash_length; + + /* + * the hashed value of the owner name + */ + public $hashed_owner_name; + + /* + * array of RR type names + */ + public $type_bit_maps = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->algorithm . ' ' . $this->flags . ' ' . $this->iterations . ' '; + + // + // per RFC5155, the salt_length value isn't displayed, and if the salt + // is empty, the salt is displayed as '-' + // + if ($this->salt_length > 0) { + + $out .= $this->salt; + } else { + + $out .= '-'; + } + + // + // per RFC5255 the hash length isn't shown + // + $out .= ' ' . $this->hashed_owner_name; + + // + // show the RR's + // + foreach ($this->type_bit_maps as $rr) { + + $out .= ' ' . strtoupper($rr); + } + + return $out; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->algorithm = array_shift($rdata); + $this->flags = array_shift($rdata); + $this->iterations = array_shift($rdata); + + // + // an empty salt is represented as '-' per RFC5155 section 3.3 + // + $salt = array_shift($rdata); + if ($salt == '-') { + + $this->salt_length = 0; + $this->salt = ''; + } else { + + $this->salt_length = strlen(pack('H*', $salt)); + $this->salt = strtoupper($salt); + } + + $this->hashed_owner_name = array_shift($rdata); + $this->hash_length = strlen(base64_decode($this->hashed_owner_name)); + + $this->type_bit_maps = $rdata; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the first values + // + $x = unpack('Calgorithm/Cflags/niterations/Csalt_length', $this->rdata); + + $this->algorithm = $x['algorithm']; + $this->flags = $x['flags']; + $this->iterations = $x['iterations']; + $this->salt_length = $x['salt_length']; + + $offset = 5; + + if ($this->salt_length > 0) { + + $x = unpack('H*', substr($this->rdata, $offset, $this->salt_length)); + $this->salt = strtoupper($x[1]); + $offset += $this->salt_length; + } + + // + // unpack the hash length + // + $x = unpack('@' . $offset . '/Chash_length', $this->rdata); + $offset++; + + // + // copy out the hash + // + $this->hash_length = $x['hash_length']; + if ($this->hash_length > 0) { + + $this->hashed_owner_name = base64_encode( + substr($this->rdata, $offset, $this->hash_length) + ); + $offset += $this->hash_length; + } + + // + // parse out the RR bitmap + // + $this->type_bit_maps = Net_DNS2_BitMap::bitMapToArray( + substr($this->rdata, $offset) + ); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + // + // pull the salt and build the length + // + $salt = pack('H*', $this->salt); + $this->salt_length = strlen($salt); + + // + // pack the algorithm, flags, iterations and salt length + // + $data = pack( + 'CCnC', + $this->algorithm, $this->flags, $this->iterations, $this->salt_length + ); + $data .= $salt; + + // + // add the hash length and hash + // + $data .= chr($this->hash_length); + if ($this->hash_length > 0) { + + $data .= base64_decode($this->hashed_owner_name); + } + + // + // conver the array of RR names to a type bitmap + // + $data .= Net_DNS2_BitMap::arrayToBitMap($this->type_bit_maps); + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/NSEC3PARAM.php b/include/pear/Net/DNS2/RR/NSEC3PARAM.php new file mode 100644 index 0000000000000000000000000000000000000000..9a67c628081cdd53665f9f583cc46fa650518f1d --- /dev/null +++ b/include/pear/Net/DNS2/RR/NSEC3PARAM.php @@ -0,0 +1,220 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: NSEC3PARAM.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * NSEC3PARAM Resource Record - RFC5155 section 4.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Hash Alg. | Flags | Iterations | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Salt Length | Salt / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_NSEC3PARAM extends Net_DNS2_RR +{ + /* + * Algorithm to use + * + * TODO: same as the NSEC3 + */ + public $algorithm; + + /* + * flags + */ + public $flags; + + /* + * defines the number of additional times the hash is performed. + */ + public $iterations; + + /* + * the length of the salt- not displayed + */ + public $salt_length; + + /* + * the salt + */ + public $salt; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->algorithm . ' ' . $this->flags . ' ' . $this->iterations . ' '; + + // + // per RFC5155, the salt_length value isn't displayed, and if the salt + // is empty, the salt is displayed as "-" + // + if ($this->salt_length > 0) { + + $out .= $this->salt; + } else { + + $out .= '-'; + } + + return $out; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->algorithm = array_shift($rdata); + $this->flags = array_shift($rdata); + $this->iterations = array_shift($rdata); + + $salt = array_shift($rdata); + if ($salt == '-') { + + $this->salt_length = 0; + $this->salt = ''; + } else { + + $this->salt_length = strlen(pack('H*', $salt)); + $this->salt = strtoupper($salt); + } + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $x = unpack('Calgorithm/Cflags/niterations/Csalt_length', $this->rdata); + + $this->algorithm = $x['algorithm']; + $this->flags = $x['flags']; + $this->iterations = $x['iterations']; + $this->salt_length = $x['salt_length']; + + if ($this->salt_length > 0) { + + $x = unpack('H*', substr($this->rdata, 5, $this->salt_length)); + $this->salt = strtoupper($x[1]); + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + $salt = pack('H*', $this->salt); + $this->salt_length = strlen($salt); + + $data = pack( + 'CCnC', + $this->algorithm, $this->flags, $this->iterations, $this->salt_length + ) . $salt; + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/OPT.php b/include/pear/Net/DNS2/RR/OPT.php new file mode 100644 index 0000000000000000000000000000000000000000..37422d689f07616071d66bfd1762abf00b85ec83 --- /dev/null +++ b/include/pear/Net/DNS2/RR/OPT.php @@ -0,0 +1,292 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: OPT.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.0.0 + * + */ + +/** + * OPT Resource Record - RFC2929 section 3.1 + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | OPTION-CODE | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | OPTION-LENGTH | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | | + * / OPTION-DATA / + * / / + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_OPT extends Net_DNS2_RR +{ + /* + * option code - assigned by IANA + */ + public $option_code; + + /* + * the length of the option data + */ + public $option_length; + + /* + * the option data + */ + public $option_data; + + /* + * the extended response code stored in the TTL + */ + public $extended_rcode; + + /* + * the implementation level + */ + public $version; + + /* + * the DO bit used for DNSSEC - RFC3225 + */ + public $do; + + /* + * the extended flags + */ + public $z; + + /** + * Constructor - builds a new Net_DNS2_RR_OPT object; normally you wouldn't call + * this directly, but OPT RR's are a little different + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet or null to create + * an empty object + * @param array $rr an array with RR parse values or null to + * create an empty object + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct(Net_DNS2_Packet &$packet = null, array $rr = null) + { + // + // this is for when we're manually building an OPT RR object; we aren't + // passing in binary data to parse, we just want a clean/empty object. + // + $this->type = 'OPT'; + $this->rdlength = 0; + + $this->option_length = 0; + $this->extended_rcode = 0; + $this->version = 0; + $this->do = 0; + $this->z = 0; + + // + // everthing else gets passed through to the parent. + // + if ( (!is_null($packet)) && (!is_null($rr)) ) { + + parent::__construct($packet, $rr); + } + } + + /** + * method to return the rdata portion of the packet as a string. There is no + * defintion for returning an OPT RR by string- this is just here to validate + * the binary parsing / building routines. + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->option_code . ' ' . $this->option_data; + } + + /** + * parses the rdata portion from a standard DNS config line. There is no + * definition for parsing a OPT RR by string- this is just here to validate + * the binary parsing / building routines. + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->option_code = array_shift($rdata); + $this->option_data = array_shift($rdata); + $this->option_length = strlen($this->option_data); + + $x = unpack('Cextended/Cversion/Cdo/Cz', pack('N', $this->ttl)); + + $this->extended_rcode = $x['extended']; + $this->version = $x['version']; + $this->do = ($x['do'] >> 7); + $this->z = $x['z']; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + // + // parse out the TTL value + // + $x = unpack('Cextended/Cversion/Cdo/Cz', pack('N', $this->ttl)); + + $this->extended_rcode = $x['extended']; + $this->version = $x['version']; + $this->do = ($x['do'] >> 7); + $this->z = $x['z']; + + // + // parse the data, if there is any + // + if ($this->rdlength > 0) { + + // + // unpack the code and length + // + $x = unpack('noption_code/noption_length', $this->rdata); + + $this->option_code = $x['option_code']; + $this->option_length = $x['option_length']; + + // + // copy out the data based on the length + // + $this->option_data = substr($this->rdata, 4); + } + + return true; + } + + /** + * pre-builds the TTL value for this record; we needed to separate this out + * from the rrGet() function, as the logic in the Net_DNS2_RR packs the TTL + * value before it builds the rdata value. + * + * @return void + * @access protected + * + */ + protected function preBuild() + { + // + // build the TTL value based on the local values + // + $ttl = unpack( + 'N', + pack('CCCC', $this->extended_rcode, $this->version, ($this->do << 7), 0) + ); + + $this->ttl = $ttl[1]; + + return; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + // + // if there is an option code, then pack that data too + // + if ($this->option_code) { + + $data = pack('nn', $this->option_code, $this->option_length) . + $this->option_data; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/PTR.php b/include/pear/Net/DNS2/RR/PTR.php new file mode 100644 index 0000000000000000000000000000000000000000..cc0b90e782dbf543f43833400ee0a7babc16bfcf --- /dev/null +++ b/include/pear/Net/DNS2/RR/PTR.php @@ -0,0 +1,152 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: PTR.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * PTR Resource Record - RFC1035 section 3.3.12 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / PTRDNAME / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_PTR extends Net_DNS2_RR +{ + /* + * the hostname of the PTR entry + */ + public $ptrdname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->ptrdname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->ptrdname = $this->cleanString(array_shift($rdata)); + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + $this->ptrdname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->ptrdname) > 0) { + + return $packet->compress($this->ptrdname, $packet->offset); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/PX.php b/include/pear/Net/DNS2/RR/PX.php new file mode 100644 index 0000000000000000000000000000000000000000..143b8e879d1e5af4d548127148d7fa3fc678ee3a --- /dev/null +++ b/include/pear/Net/DNS2/RR/PX.php @@ -0,0 +1,186 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: PX.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * PX Resource Record - RFC2163 section 4 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PREFERENCE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / MAP822 / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / MAPX400 / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-- + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_PX extends Net_DNS2_RR +{ + /* + * preference + */ + public $preference; + + /* + * the RFC822 part of the MCGAM + */ + public $map822; + + /* + * the X.400 part of the MCGAM + */ + public $mapx400; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . $this->cleanString($this->map822) . '. ' . + $this->cleanString($this->mapx400) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = $rdata[0]; + $this->map822 = $this->cleanString($rdata[1]); + $this->mapx400 = $this->cleanString($rdata[2]); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse the preference + // + $x = unpack('npreference', $this->rdata); + $this->preference = $x['preference']; + + $offset = $packet->offset + 2; + + $this->map822 = Net_DNS2_Packet::expand($packet, $offset); + $this->mapx400 = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->map822) > 0) { + + $data = pack('n', $this->preference); + $packet->offset += 2; + + $data .= $packet->compress($this->map822, $packet->offset); + $data .= $packet->compress($this->mapx400, $packet->offset); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/RP.php b/include/pear/Net/DNS2/RR/RP.php new file mode 100644 index 0000000000000000000000000000000000000000..f0b97b7ab968a4923ad0a4c7129b407967289b44 --- /dev/null +++ b/include/pear/Net/DNS2/RR/RP.php @@ -0,0 +1,167 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: RP.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * RP Resource Record - RFC1183 section 2.2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / mboxdname / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / txtdname / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_RP extends Net_DNS2_RR +{ + /* + * mailbox for the responsible person + */ + public $mboxdname; + + /* + * is a domain name for which TXT RR's exists + */ + public $txtdname; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->mboxdname) . '. ' . + $this->cleanString($this->txtdname) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->mboxdname = $this->cleanString($rdata[0]); + $this->txtdname = $this->cleanString($rdata[1]); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + + $this->mboxdname = Net_DNS2_Packet::expand($packet, $offset); + $this->txtdname = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->mboxdname) > 0) { + + return $packet->compress($this->mboxdname, $packet->offset) . + $packet->compress($this->txtdname, $packet->offset); + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/RRSIG.php b/include/pear/Net/DNS2/RR/RRSIG.php new file mode 100644 index 0000000000000000000000000000000000000000..6f9aa276e24664607c3ec5db2c5b73725c2a488b --- /dev/null +++ b/include/pear/Net/DNS2/RR/RRSIG.php @@ -0,0 +1,329 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: RRSIG.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + * This file contains code based off the Net::DNS::SEC Perl module by + * Olaf M. Kolkman + * + * This is the copyright notice from the PERL Net::DNS::SEC module: + * + * Copyright (c) 2001 - 2005 RIPE NCC. Author Olaf M. Kolkman + * Copyright (c) 2007 - 2008 NLnet Labs. Author Olaf M. Kolkman + * <olaf@net-dns.org> + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of the author not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL + * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/** + * RRSIG Resource Record - RFC4034 sction 3.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type Covered | Algorithm | Labels | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Original TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Signature Expiration | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Signature Inception | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Key Tag | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Signer's Name / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / / + * / Signature / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_RRSIG extends Net_DNS2_RR +{ + /* + * the RR type covered by this signature + */ + public $typecovered; + + /* + * the algorithm used for the signature + */ + public $algorithm; + + /* + * the number of labels in the name + */ + public $labels; + + /* + * the original TTL + */ + public $origttl; + + /* + * the signature expiration + */ + public $sigexp; + + /* + * the inception of the signature + */ + public $sigincep; + + /* + * the keytag used + */ + public $keytag; + + /* + * the signer's name + */ + public $signname; + + /* + * the signature + */ + public $signature; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->typecovered . ' ' . $this->algorithm . ' ' . + $this->labels . ' ' . $this->origttl . ' ' . + $this->sigexp . ' ' . $this->sigincep . ' ' . + $this->keytag . ' ' . $this->cleanString($this->signname) . '. ' . + $this->signature; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->typecovered = strtoupper(array_shift($rdata)); + $this->algorithm = array_shift($rdata); + $this->labels = array_shift($rdata); + $this->origttl = array_shift($rdata); + $this->sigexp = array_shift($rdata); + $this->sigincep = array_shift($rdata); + $this->keytag = array_shift($rdata); + $this->signname = $this->cleanString(array_shift($rdata)); + + foreach ($rdata as $line) { + + $this->signature .= $line; + } + + $this->signature = trim($this->signature); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack + // + $x = unpack( + 'ntc/Calgorithm/Clabels/Norigttl/Nsigexp/Nsigincep/nkeytag', + $this->rdata + ); + + $this->typecovered = Net_DNS2_Lookups::$rr_types_by_id[$x['tc']]; + $this->algorithm = $x['algorithm']; + $this->labels = $x['labels']; + $this->origttl = Net_DNS2::expandUint32($x['origttl']); + + // + // the dates are in GM time + // + $this->sigexp = gmdate('YmdHis', $x['sigexp']); + $this->sigincep = gmdate('YmdHis', $x['sigincep']); + + // + // get the keytag + // + $this->keytag = $x['keytag']; + + // + // get teh signers name and signature + // + $offset = $packet->offset + 18; + $sigoffset = $offset; + + $this->signname = strtolower( + Net_DNS2_Packet::expand($packet, $sigoffset) + ); + $this->signature = base64_encode( + substr($this->rdata, 18 + ($sigoffset - $offset)) + ); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->signature) > 0) { + + // + // parse the values out of the dates + // + preg_match( + '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigexp, $e + ); + preg_match( + '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigincep, $i + ); + + // + // pack the value + // + $data = pack( + 'nCCNNNn', + Net_DNS2_Lookups::$rr_types_by_name[$this->typecovered], + $this->algorithm, + $this->labels, + $this->origttl, + gmmktime($e[4], $e[5], $e[6], $e[2], $e[3], $e[1]), + gmmktime($i[4], $i[5], $i[6], $i[2], $i[3], $i[1]), + $this->keytag + ); + + // + // the signer name is special; it's not allowed to be compressed + // (see section 3.1.7) + // + $names = explode('.', strtolower($this->signname)); + foreach ($names as $name) { + + $data .= chr(strlen($name)); + $data .= $name; + } + $data .= "\0"; + + // + // add the signature + // + $data .= base64_decode($this->signature); + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/RT.php b/include/pear/Net/DNS2/RR/RT.php new file mode 100644 index 0000000000000000000000000000000000000000..afbe966951cbb5295c20db024c8a96d35fcecbf7 --- /dev/null +++ b/include/pear/Net/DNS2/RR/RT.php @@ -0,0 +1,175 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: RT.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * RT Resource Record - RFC1183 section 3.3 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | preference | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / intermediate-host / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_RT extends Net_DNS2_RR +{ + /* + * the preference of this route + */ + public $preference; + + /* + * host which will servce as an intermediate in reaching the owner host + */ + public $intermediatehost; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->preference . ' ' . + $this->cleanString($this->intermediatehost) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->preference = $rdata[0]; + $this->intermediatehost = $this->cleanString($rdata[1]); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the preference + // + $x = unpack('npreference', $this->rdata); + + $this->preference = $x['preference']; + $offset = $packet->offset + 2; + + $this->intermediatehost = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->intermediatehost) > 0) { + + $data = pack('n', $this->preference); + $packet->offset += 2; + + $data .= $packet->compress($this->intermediatehost, $packet->offset); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/SIG.php b/include/pear/Net/DNS2/RR/SIG.php new file mode 100644 index 0000000000000000000000000000000000000000..4d6202066d0c6ecd425e02c4f8e76fc94c9d13a8 --- /dev/null +++ b/include/pear/Net/DNS2/RR/SIG.php @@ -0,0 +1,423 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: SIG.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + * This file contains code based off the Net::DNS::SEC Perl module by + * Olaf M. Kolkman + * + * This is the copyright notice from the PERL Net::DNS::SEC module: + * + * Copyright (c) 2001 - 2005 RIPE NCC. Author Olaf M. Kolkman + * Copyright (c) 2007 - 2008 NLnet Labs. Author Olaf M. Kolkman + * <olaf@net-dns.org> + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of the author not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL + * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/** + * SIG Resource Record - RFC2535 section 4.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type Covered | Algorithm | Labels | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Original TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Signature Expiration | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Signature Inception | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Key Tag | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Signer's Name / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / / + * / Signature / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_SIG extends Net_DNS2_RR +{ + /* + * and instance of a Net_DNS2_PrivateKey object + */ + public $private_key = null; + + /* + * the RR type covered by this signature + */ + public $typecovered; + + /* + * the algorithm used for the signature + */ + public $algorithm; + + /* + * the number of labels in the name + */ + public $labels; + + /* + * the original TTL + */ + public $origttl; + + /* + * the signature expiration + */ + public $sigexp; + + /* + * the inception of the signature + */ + public $sigincep; + + /* + * the keytag used + */ + public $keytag; + + /* + * the signer's name + */ + public $signname; + + /* + * the signature + */ + public $signature; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->typecovered . ' ' . $this->algorithm . ' ' . + $this->labels . ' ' . $this->origttl . ' ' . + $this->sigexp . ' ' . $this->sigincep . ' ' . + $this->keytag . ' ' . $this->cleanString($this->signname) . '. ' . + $this->signature; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->typecovered = strtoupper(array_shift($rdata)); + $this->algorithm = array_shift($rdata); + $this->labels = array_shift($rdata); + $this->origttl = array_shift($rdata); + $this->sigexp = array_shift($rdata); + $this->sigincep = array_shift($rdata); + $this->keytag = array_shift($rdata); + $this->signname = $this->cleanString(array_shift($rdata)); + + foreach ($rdata as $line) { + + $this->signature .= $line; + } + + $this->signature = trim($this->signature); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack + // + $x = unpack( + 'ntc/Calgorithm/Clabels/Norigttl/Nsigexp/Nsigincep/nkeytag', + $this->rdata + ); + + $this->typecovered = Net_DNS2_Lookups::$rr_types_by_id[$x['tc']]; + $this->algorithm = $x['algorithm']; + $this->labels = $x['labels']; + $this->origttl = Net_DNS2::expandUint32($x['origttl']); + + // + // the dates are in GM time + // + $this->sigexp = gmdate('YmdHis', $x['sigexp']); + $this->sigincep = gmdate('YmdHis', $x['sigincep']); + + // + // get the keytag + // + $this->keytag = $x['keytag']; + + // + // get teh signers name and signature + // + $offset = $packet->offset + 18; + $sigoffset = $offset; + + $this->signname = strtolower( + Net_DNS2_Packet::expand($packet, $sigoffset) + ); + $this->signature = base64_encode( + substr($this->rdata, 18 + ($sigoffset - $offset)) + ); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + // + // parse the values out of the dates + // + preg_match( + '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigexp, $e + ); + preg_match( + '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigincep, $i + ); + + // + // pack the value + // + $data = pack( + 'nCCNNNn', + Net_DNS2_Lookups::$rr_types_by_name[$this->typecovered], + $this->algorithm, + $this->labels, + $this->origttl, + gmmktime($e[4], $e[5], $e[6], $e[2], $e[3], $e[1]), + gmmktime($i[4], $i[5], $i[6], $i[2], $i[3], $i[1]), + $this->keytag + ); + + // + // the signer name is special; it's not allowed to be compressed + // (see section 3.1.7) + // + $names = explode('.', strtolower($this->signname)); + foreach ($names as $name) { + + $data .= chr(strlen($name)); + $data .= $name; + } + + $data .= chr('0'); + + // + // if the signature is empty, and $this->private_key is an instance of a + // private key object, and we have access to openssl, then assume this + // is a SIG(0), and generate a new signature + // + if ( (strlen($this->signature) == 0) + && ($this->private_key instanceof Net_DNS2_PrivateKey) + && (extension_loaded('openssl') === true) + ) { + + // + // create a new packet for the signature- + // + $new_packet = new Net_DNS2_Packet_Request('example.com', 'SOA', 'IN'); + + // + // copy the packet data over + // + $new_packet->copy($packet); + + // + // remove the SIG object from the additional list + // + array_pop($new_packet->additional); + $new_packet->header->arcount = count($new_packet->additional); + + // + // copy out the data + // + $sigdata = $data . $new_packet->get(); + + // + // based on the algorithm + // + $algorithm = 0; + + switch($this->algorithm) { + + // + // MD5 + // + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: + + $algorithm = OPENSSL_ALGO_MD5; + break; + + // + // SHA1 + // + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: + + $algorithm = OPENSSL_ALGO_SHA1; + break; + + // + // un-supported + // + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_DSA: + // + // DSA won't work in PHP until the OpenSSL extension has + // better DSA support + // + case Net_DNS2_Lookups::DSNSEC_ALGORITHM_RSASHA1NSEC3SHA1: + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA256: + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA512: + case Net_DNS2_Lookups::DNSSEC_ALGORITHM_DSANSEC3SHA1: + default: + throw new Net_DNS2_Exception( + 'invalid or unsupported algorithm', + Net_DNS2_Lookups::E_OPENSSL_INV_ALGO + ); + break; + } + + // + // sign the data + // + if (openssl_sign( + $sigdata, $this->signature, $this->private_key->instance, $algorithm + ) == false) { + + throw new Net_DNS2_Exception( + openssl_error_string(), + Net_DNS2_Lookups::E_OPENSSL_ERROR + ); + } + + // + // add it locally encoded + // + $this->signature = base64_encode($this->signature); + } + + // + // add the signature + // + $data .= base64_decode($this->signature); + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/SOA.php b/include/pear/Net/DNS2/RR/SOA.php new file mode 100644 index 0000000000000000000000000000000000000000..5f92f87407e01f148e34ebe4a58d9e5aababa375 --- /dev/null +++ b/include/pear/Net/DNS2/RR/SOA.php @@ -0,0 +1,240 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: SOA.php 149 2012-03-02 01:08:19Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * SOA Resource Record - RFC1035 section 3.3.13 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / MNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / RNAME / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | SERIAL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | REFRESH | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RETRY | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | EXPIRE | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | MINIMUM | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_SOA extends Net_DNS2_RR +{ + /* + * The master DNS server + */ + public $mname; + + /* + * mailbox of the responsible person + */ + public $rname; + + /* + * serial number + */ + public $serial; + + /* + * refresh time + */ + public $refresh; + + /* + * retry interval + */ + public $retry; + + /* + * expire time + */ + public $expire; + + /* + * minimum TTL for any RR in this zone + */ + public $minimum; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->mname) . '. ' . + $this->cleanString($this->rname) . '. ' . + $this->serial . ' ' . $this->refresh . ' ' . $this->retry . ' ' . + $this->expire . ' ' . $this->minimum; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->mname = $this->cleanString($rdata[0]); + $this->rname = $this->cleanString($rdata[1]); + + $this->serial = $rdata[2]; + $this->refresh = $rdata[3]; + $this->retry = $rdata[4]; + $this->expire = $rdata[5]; + $this->minimum = $rdata[6]; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // parse the + // + $offset = $packet->offset; + + $this->mname = Net_DNS2_Packet::expand($packet, $offset); + $this->rname = Net_DNS2_Packet::expand($packet, $offset); + + // + // get the SOA values + // + $x = unpack( + '@' . $offset . '/Nserial/Nrefresh/Nretry/Nexpire/Nminimum/', + $packet->rdata + ); + + $this->serial = Net_DNS2::expandUint32($x['serial']); + $this->refresh = Net_DNS2::expandUint32($x['refresh']); + $this->retry = Net_DNS2::expandUint32($x['retry']); + $this->expire = Net_DNS2::expandUint32($x['expire']); + $this->minimum = Net_DNS2::expandUint32($x['minimum']); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->mname) > 0) { + + $data = $packet->compress($this->mname, $packet->offset); + $data .= $packet->compress($this->rname, $packet->offset); + + $data .= pack( + 'N5', $this->serial, $this->refresh, $this->retry, + $this->expire, $this->minimum + ); + + $packet->offset += 20; + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/SPF.php b/include/pear/Net/DNS2/RR/SPF.php new file mode 100644 index 0000000000000000000000000000000000000000..751d93ac808dedf27a01c9e57915c298150532ce --- /dev/null +++ b/include/pear/Net/DNS2/RR/SPF.php @@ -0,0 +1,75 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: SPF.php 47 2010-10-24 23:53:08Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * The SPF RR is implemented exactly like the TXT record, so + * for now we just extend the TXT RR and use it. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_SPF extends Net_DNS2_RR_TXT +{ +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/SRV.php b/include/pear/Net/DNS2/RR/SRV.php new file mode 100644 index 0000000000000000000000000000000000000000..616be99441059adea75f4d5078ea82d69d5e43f0 --- /dev/null +++ b/include/pear/Net/DNS2/RR/SRV.php @@ -0,0 +1,186 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: SRV.php 127 2011-12-03 03:29:39Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * SRV Resource Record - RFC2782 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PRIORITY | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | WEIGHT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PORT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / TARGET / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_SRV extends Net_DNS2_RR +{ + /* + * The priority of this target host. + */ + public $priority; + + /* + * a relative weight for entries with the same priority + */ + public $weight; + + /* + * The port on this target host of this service. + */ + public $port; + + /* + * The domain name of the target host + */ + public $target; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->priority . ' ' . $this->weight . ' ' . + $this->port . ' ' . $this->cleanString($this->target) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->priority = $rdata[0]; + $this->weight = $rdata[1]; + $this->port = $rdata[2]; + + $this->target = $this->cleanString($rdata[3]); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the priority, weight and port + // + $x = unpack('npriority/nweight/nport', $this->rdata); + + $this->priority = $x['priority']; + $this->weight = $x['weight']; + $this->port = $x['port']; + + $offset = $packet->offset + 6; + $this->target = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->target) > 0) { + + $data = pack('nnn', $this->priority, $this->weight, $this->port); + $packet->offset += 6; + + $data .= $packet->compress($this->target, $packet->offset); + + return $data; + } + + return null; + } +} + +?> diff --git a/include/pear/Net/DNS2/RR/SSHFP.php b/include/pear/Net/DNS2/RR/SSHFP.php new file mode 100644 index 0000000000000000000000000000000000000000..13022a6b87bca94252108a285d0524b58484c141 --- /dev/null +++ b/include/pear/Net/DNS2/RR/SSHFP.php @@ -0,0 +1,244 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: SSHFP.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * SSHFP Resource Record - RFC4255 section 3.1 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | algorithm | fp type | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / + * / / + * / fingerprint / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_SSHFP extends Net_DNS2_RR +{ + /* + * the algorithm used + */ + public $algorithm; + + /* + * The finger print type + */ + public $fp_type; + + /* + * the finger print data + */ + public $fingerprint; + + /* + * Algorithms + */ + const SSHFP_ALGORITHM_RES = 0; + const SSHFP_ALGORITHM_RSA = 1; + const SSHFP_ALGORITHM_DSS = 2; + + /* + * Fingerprint Types + */ + const SSHFP_FPTYPE_RES = 0; + const SSHFP_FPTYPE_SHA1 = 1; + + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->algorithm . ' ' . $this->fp_type . ' ' . $this->fingerprint; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // "The use of mnemonics instead of numbers is not allowed." + // + // RFC4255 section 3.2 + // + $algorithm = array_shift($rdata); + $fp_type = array_shift($rdata); + $fingerprint = strtolower(implode('', $rdata)); + + // + // There are only two algorithm's defined + // + if ( ($algorithm != self::SSHFP_ALGORITHM_RSA) + && ($algorithm != self::SSHFP_ALGORITHM_DSS) + ) { + return false; + } + + // + // there's only one fingerprint type currently implemented, so if it's not + // that, then fail. + // + if ($fp_type != self::SSHFP_FPTYPE_SHA1) { + return false; + } + + $this->algorithm = $algorithm; + $this->fp_type = $fp_type; + $this->fingerprint = $fingerprint; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the algorithm and finger print type + // + $x = unpack('Calgorithm/Cfp_type', $this->rdata); + + $this->algorithm = $x['algorithm']; + $this->fp_type = $x['fp_type']; + + // + // There are only two algorithm's defined + // + if ( ($this->algorithm != self::SSHFP_ALGORITHM_RSA) + && ($this->algorithm != self::SSHFP_ALGORITHM_DSS) + ) { + return false; + } + + // + // there's only one fingerprint type currently implemented, + // so if it's not that, then fail. + // + if ($this->fp_type != self::SSHFP_FPTYPE_SHA1) { + return false; + } + + // + // parse the finger print; this assumes SHA-1 + // + $fp = unpack('H*a', substr($this->rdata, 2)); + $this->fingerprint = strtolower($fp['a']); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->fingerprint) > 0) { + + $data = pack( + 'CCH*', $this->algorithm, $this->fp_type, $this->fingerprint + ); + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TA.php b/include/pear/Net/DNS2/RR/TA.php new file mode 100644 index 0000000000000000000000000000000000000000..0253dbe52099ffc4826a1762ed626cc8fc8c34b9 --- /dev/null +++ b/include/pear/Net/DNS2/RR/TA.php @@ -0,0 +1,75 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2011, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TA.php 130 2011-12-03 05:02:37Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.0 + * + */ + +/** + * The TA RR is implemented exactly like the DS record, so + * for now we just extend the DS RR and use it. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TA extends Net_DNS2_RR_DS +{ +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TALINK.php b/include/pear/Net/DNS2/RR/TALINK.php new file mode 100644 index 0000000000000000000000000000000000000000..33e938c5144d76caed2903478ae4f4499fe35456 --- /dev/null +++ b/include/pear/Net/DNS2/RR/TALINK.php @@ -0,0 +1,171 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2011, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TALINK.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.0 + * + */ + +/** + * TALINK Resource Record - DNSSEC Trust Anchor + * + * http://tools.ietf.org/id/draft-ietf-dnsop-dnssec-trust-history-00.txt + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / PREVIOUS / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / NEXT / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TALINK extends Net_DNS2_RR +{ + /* + * the previous domain name + */ + public $previous; + + /* + * the next domain name + */ + public $next; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cleanString($this->previous) . '. ' . + $this->cleanString($this->next) . '.'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->previous = $this->cleanString($rdata[0]); + $this->next = $this->cleanString($rdata[1]); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $offset = $packet->offset; + + $this->previous = Net_DNS2_Packet::label($packet, $offset); + $this->next = Net_DNS2_Packet::label($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if ( (strlen($this->previous) > 0) || (strlen($this->next) > 0) ) { + + $data = chr(strlen($this->previous)) . $this->previous . + chr(strlen($this->next)) . $this->next; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TKEY.php b/include/pear/Net/DNS2/RR/TKEY.php new file mode 100644 index 0000000000000000000000000000000000000000..115b8bb39ac8fbed242bfce044519686dce6e13c --- /dev/null +++ b/include/pear/Net/DNS2/RR/TKEY.php @@ -0,0 +1,307 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TKEY.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * TKEY Resource Record - RFC 2930 section 2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / ALGORITHM / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | INCEPTION | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | EXPIRATION | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | MODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ERROR | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | KEY SIZE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / KEY DATA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | OTHER SIZE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / OTHER DATA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TKEY extends Net_DNS2_RR +{ + public $algorithm; + public $inception; + public $expiration; + public $mode; + public $error; + public $key_size; + public $key_data; + public $other_size; + public $other_data; + + /* + * TSIG Modes + */ + const TSIG_MODE_RES = 0; + const TSIG_MODE_SERV_ASSIGN = 1; + const TSIG_MODE_DH = 2; + const TSIG_MODE_GSS_API = 3; + const TSIG_MODE_RESV_ASSIGN = 4; + const TSIG_MODE_KEY_DELE = 5; + + /* + * map the mod id's to names so we can validate + */ + public $tsgi_mode_id_to_name = array( + + self::TSIG_MODE_RES => 'Reserved', + self::TSIG_MODE_SERV_ASSIGN => 'Server Assignment', + self::TSIG_MODE_DH => 'Diffie-Hellman', + self::TSIG_MODE_GSS_API => 'GSS-API', + self::TSIG_MODE_RESV_ASSIGN => 'Resolver Assignment', + self::TSIG_MODE_KEY_DELE => 'Key Deletion' + ); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->cleanString($this->algorithm) . '. ' . $this->mode; + if ($this->key_size > 0) { + + $out .= ' ' . trim($this->key_data, '.') . '.'; + } else { + + $out .= ' .'; + } + + return $out; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // data passed in is assumed: <algorithm> <mode> <key> + // + $this->algorithm = $this->cleanString(array_shift($rdata)); + $this->mode = array_shift($rdata); + $this->key_data = trim(array_shift($rdata), '.'); + + // + // the rest of the data is set manually + // + $this->inception = time(); + $this->expiration = time() + 86400; // 1 day + $this->error = 0; + $this->key_size = strlen($this->key_data); + $this->other_size = 0; + $this->other_data = ''; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // expand the algorithm + // + $offset = $packet->offset; + $this->algorithm = Net_DNS2_Packet::expand($packet, $offset); + + // + // unpack inception, expiration, mode, error and key size + // + $x = unpack( + '@' . $offset . '/Ninception/Nexpiration/nmode/nerror/nkey_size', + $packet->rdata + ); + + $this->inception = Net_DNS2::expandUint32($x['inception']); + $this->expiration = Net_DNS2::expandUint32($x['expiration']); + $this->mode = $x['mode']; + $this->error = $x['error']; + $this->key_size = $x['key_size']; + + $offset += 14; + + // + // if key_size > 0, then copy out the key + // + if ($this->key_size > 0) { + + $this->key_data = substr($packet->rdata, $offset, $this->key_size); + $offset += $this->key_size; + } + + // + // unpack the other length + // + $x = unpack('@' . $offset . '/nother_size', $packet->rdata); + + $this->other_size = $x['other_size']; + $offset += 2; + + // + // if other_size > 0, then copy out the data + // + if ($this->other_size > 0) { + + $this->other_data = substr( + $packet->rdata, $offset, $this->other_size + ); + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->algorithm) > 0) { + + // + // make sure the size values are correct + // + $this->key_size = strlen($this->key_data); + $this->other_size = strlen($this->other_data); + + // + // add the algorithm without compression + // + $data = Net_DNS2_Packet::pack($this->algorithm); + + // + // pack in the inception, expiration, mode, error and key size + // + $data .= pack( + 'NNnnn', $this->inception, $this->expiration, + $this->mode, 0, $this->key_size + ); + + // + // if the key_size > 0, then add the key + // + if ($this->key_size > 0) { + + $data .= $this->key_data; + } + + // + // pack in the other size + // + $data .= pack('n', $this->other_size); + if ($this->other_size > 0) { + + $data .= $this->other_data; + } + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TLSA.php b/include/pear/Net/DNS2/RR/TLSA.php new file mode 100644 index 0000000000000000000000000000000000000000..5eba7b5b19dc4a2909804c8fda0f6a2bbf330963 --- /dev/null +++ b/include/pear/Net/DNS2/RR/TLSA.php @@ -0,0 +1,194 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2012, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2012 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TLSA.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.5 + * + */ + +/** + * TLSA Resource Record - RFC 6698 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Cert. Usage | Selector | Matching Type | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / + * / / + * / Certificate Association Data / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TLSA extends Net_DNS2_RR +{ + /* + * The Certificate Usage Field + */ + public $cert_usage; + + /* + * The Selector Field + */ + public $selector; + + /* + * The Matching Type Field + */ + public $matching_type; + + /* + * The Certificate Association Data Field + */ + public $certificate; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->cert_usage . ' ' . $this->selector . ' ' . + $this->matching_type . ' ' . base64_encode($this->certificate); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->cert_usage = array_shift($rdata); + $this->selector = array_shift($rdata); + $this->matching_type = array_shift($rdata); + $this->certificate = base64_decode(implode('', $rdata)); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the format, keytag and algorithm + // + $x = unpack('Cusage/Cselector/Ctype', $this->rdata); + + $this->cert_usage = $x['usage']; + $this->selector = $x['selector']; + $this->matching_type = $x['type']; + + // + // copy the certificate + // + $this->certificate = substr($this->rdata, 3, $this->rdlength - 3); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->certificate) > 0) { + + $data = pack( + 'CCC', $this->cert_usage, $this->selector, $this->matching_type + ) . $this->certificate; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TSIG.php b/include/pear/Net/DNS2/RR/TSIG.php new file mode 100644 index 0000000000000000000000000000000000000000..472107585b3c5cb27d63c9223088dbb8dcc27298 --- /dev/null +++ b/include/pear/Net/DNS2/RR/TSIG.php @@ -0,0 +1,504 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TSIG.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * TSIG Resource Record - RFC 2845 + * + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / algorithm / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time signed | + * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | fudge | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | mac size | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / + * / mac / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | original id | error | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | other length | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / + * / other data / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TSIG extends Net_DNS2_RR +{ + /* + * TSIG Algorithm Identifiers + */ + const HMAC_MD5 = 'hmac-md5.sig-alg.reg.int'; // RFC 2845, required + const GSS_TSIG = 'gss-tsig'; // unsupported, optional + const HMAC_SHA1 = 'hmac-sha1'; // RFC 4635, required + const HMAC_SHA224 = 'hmac-sha224'; // RFC 4635, optional + const HMAC_SHA256 = 'hmac-sha256'; // RFC 4635, required + const HMAC_SHA384 = 'hmac-sha384'; // RFC 4635, optional + const HMAC_SHA512 = 'hmac-sha512'; // RFC 4635, optional + + /* + * the map of hash values to names + */ + public static $hash_algorithms = array( + + self::HMAC_MD5 => 'md5', + self::HMAC_SHA1 => 'sha1', + self::HMAC_SHA224 => 'sha224', + self::HMAC_SHA256 => 'sha256', + self::HMAC_SHA384 => 'sha384', + self::HMAC_SHA512 => 'sha512' + ); + + /* + * algorithm used; only supports HMAC-MD5 + */ + public $algorithm; + + /* + * The time it was signed + */ + public $time_signed; + + /* + * fudge- allowed offset from the time signed + */ + public $fudge; + + /* + * size of the digest + */ + public $mac_size; + + /* + * the digest data + */ + public $mac; + + /* + * the original id of the request + */ + public $original_id; + + /* + * additional error code + */ + public $error; + + /* + * length of the "other" data, should only ever be 0 when there is + * no error, or 6 when there is the error RCODE_BADTIME + */ + public $other_length; + + /* + * the other data; should only ever be a timestamp when there is the + * error RCODE_BADTIME + */ + public $other_data; + + /* + * the key to use for signing - passed in, not included in the rdata + */ + public $key; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $out = $this->cleanString($this->algorithm) . '. ' . + $this->time_signed . ' ' . + $this->fudge . ' ' . $this->mac_size . ' ' . + base64_encode($this->mac) . ' ' . $this->original_id . ' ' . + $this->error . ' '. $this->other_length; + + if ($this->other_length > 0) { + + $out .= ' ' . $this->other_data; + } + + return $out; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + // + // the only value passed in is the key- + // + // this assumes it's passed in base64 encoded. + // + $this->key = preg_replace('/\s+/', '', array_shift($rdata)); + + // + // the rest of the data is set to default + // + $this->algorithm = self::HMAC_MD5; + $this->time_signed = time(); + $this->fudge = 300; + $this->mac_size = 0; + $this->mac = ''; + $this->original_id = 0; + $this->error = 0; + $this->other_length = 0; + $this->other_data = ''; + + // + // per RFC 2845 section 2.3 + // + $this->class = 'ANY'; + $this->ttl = 0; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // expand the algorithm + // + $newoffset = $packet->offset; + $this->algorithm = Net_DNS2_Packet::expand($packet, $newoffset); + $offset = $newoffset - $packet->offset; + + // + // unpack time, fudge and mac_size + // + $x = unpack( + '@' . $offset . '/ntime_high/Ntime_low/nfudge/nmac_size', + $this->rdata + ); + + $this->time_signed = Net_DNS2::expandUint32($x['time_low']); + $this->fudge = $x['fudge']; + $this->mac_size = $x['mac_size']; + + $offset += 10; + + // + // copy out the mac + // + if ($this->mac_size > 0) { + + $this->mac = substr($this->rdata, $offset, $this->mac_size); + $offset += $this->mac_size; + } + + // + // unpack the original id, error, and other_length values + // + $x = unpack( + '@' . $offset . '/noriginal_id/nerror/nother_length', + $this->rdata + ); + + $this->original_id = $x['original_id']; + $this->error = $x['error']; + $this->other_length = $x['other_length']; + + // + // the only time there is actually any "other data", is when there's + // a BADTIME error code. + // + // The other length should be 6, and the other data field includes the + // servers current time - per RFC 2845 section 4.5.2 + // + if ($this->error == Net_DNS2_Lookups::RCODE_BADTIME) { + + if ($this->other_length != 6) { + + return false; + } + + // + // other data is a 48bit timestamp + // + $x = unpack( + 'nhigh/nlow', + substr($this->rdata, $offset + 6, $this->other_length) + ); + $this->other_data = $x['low']; + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->key) > 0) { + + // + // create a new packet for the signature- + // + $new_packet = new Net_DNS2_Packet_Request('example.com', 'SOA', 'IN'); + + // + // copy the packet data over + // + $new_packet->copy($packet); + + // + // remove the TSIG object from the additional list + // + array_pop($new_packet->additional); + $new_packet->header->arcount = count($new_packet->additional); + + // + // copy out the data + // + $sig_data = $new_packet->get(); + + // + // add the name without compressing + // + $sig_data .= Net_DNS2_Packet::pack($this->name); + + // + // add the class and TTL + // + $sig_data .= pack( + 'nN', Net_DNS2_Lookups::$classes_by_name[$this->class], $this->ttl + ); + + // + // add the algorithm name without compression + // + $sig_data .= Net_DNS2_Packet::pack(strtolower($this->algorithm)); + + // + // add the rest of the values + // + $sig_data .= pack( + 'nNnnn', 0, $this->time_signed, $this->fudge, + $this->error, $this->other_length + ); + if ($this->other_length > 0) { + + $sig_data .= pack('nN', 0, $this->other_data); + } + + // + // sign the data + // + $this->mac = $this->_signHMAC( + $sig_data, base64_decode($this->key), $this->algorithm + ); + $this->mac_size = strlen($this->mac); + + // + // compress the algorithm + // + $data = Net_DNS2_Packet::pack(strtolower($this->algorithm)); + + // + // pack the time, fudge and mac size + // + $data .= pack( + 'nNnn', 0, $this->time_signed, $this->fudge, $this->mac_size + ); + $data .= $this->mac; + + // + // check the error and other_length + // + if ($this->error == Net_DNS2_Lookups::RCODE_BADTIME) { + + $this->other_length = strlen($this->other_data); + if ($this->other_length != 6) { + + return null; + } + } else { + + $this->other_length = 0; + $this->other_data = ''; + } + + // + // pack the id, error and other_length + // + $data .= pack( + 'nnn', $packet->header->id, $this->error, $this->other_length + ); + if ($this->other_length > 0) { + + $data .= pack('nN', 0, $this->other_data); + } + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } + + /** + * signs the given data with the given key, and returns the result + * + * @param string $data the data to sign + * @param string $key key to use for signing + * @param string $algorithm the algorithm to use; defaults to MD5 + * + * @return string the signed digest + * @throws Net_DNS2_Exception + * @access private + * + */ + private function _signHMAC($data, $key = null, $algorithm = self::HMAC_MD5) + { + // + // use the hash extension; this is included by default in >= 5.1.2 which + // is our dependent version anyway- so it's easy to switch to it. + // + if (extension_loaded('hash')) { + + if (!isset(self::$hash_algorithms[$algorithm])) { + + throw new Net_DNS2_Exception( + 'invalid or unsupported algorithm', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + return hash_hmac(self::$hash_algorithms[$algorithm], $data, $key, true); + } + + // + // if the hash extension isn't loaded, and they selected something other + // than MD5, throw an exception + // + if ($algorithm != self::HMAC_MD5) { + + throw new Net_DNS2_Exception( + 'only HMAC-MD5 supported. please install the php-extension ' . + '"hash" in order to use the sha-family', + Net_DNS2_Lookups::E_PARSE_ERROR + ); + } + + // + // otherwise, do it ourselves + // + if (is_null($key)) { + + return pack('H*', md5($data)); + } + + $key = str_pad($key, 64, chr(0x00)); + if (strlen($key) > 64) { + + $key = pack('H*', md5($key)); + } + + $k_ipad = $key ^ str_repeat(chr(0x36), 64); + $k_opad = $key ^ str_repeat(chr(0x5c), 64); + + return $this->_signHMAC( + $k_opad . pack('H*', md5($k_ipad . $data)), null, $algorithm + ); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/TXT.php b/include/pear/Net/DNS2/RR/TXT.php new file mode 100644 index 0000000000000000000000000000000000000000..3e86ce7e35415818a726e4b0da8372fcac930a93 --- /dev/null +++ b/include/pear/Net/DNS2/RR/TXT.php @@ -0,0 +1,177 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: TXT.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * TXT Resource Record - RFC1035 section 3.3.14 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / TXT-DATA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_TXT extends Net_DNS2_RR +{ + /* + * an array of the text strings + */ + public $text = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + if (count($this->text) == 0) { + return '""'; + } + + $data = ''; + + foreach ($this->text as $t) { + + $data .= $this->formatString($t) . ' '; + } + + return trim($data); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = $this->buildString($rdata); + if (count($data) > 0) { + + $this->text = $data; + } + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $length = $packet->offset + $this->rdlength; + $offset = $packet->offset; + + while ($length > $offset) { + + $this->text[] = Net_DNS2_Packet::label($packet, $offset); + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + $data = null; + + foreach ($this->text as $t) { + + $data .= chr(strlen($t)) . $t; + } + + $packet->offset += strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/URI.php b/include/pear/Net/DNS2/RR/URI.php new file mode 100644 index 0000000000000000000000000000000000000000..aef788b823d8be9e641d0ac041b4b89ee6fcf09e --- /dev/null +++ b/include/pear/Net/DNS2/RR/URI.php @@ -0,0 +1,183 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2011, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2011 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: URI.php 132 2011-12-03 05:28:54Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.2.0 + * + */ + +/** + * URI Resource Record - http://tools.ietf.org/html/draft-faltstrom-uri-06 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PRIORITY | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | WEIGHT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / TARGET / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_URI extends Net_DNS2_RR +{ + /* + * The priority of this target host. + */ + public $priority; + + /* + * a relative weight for entries with the same priority + */ + public $weight; + + /* + * The domain name of the target host + */ + public $target; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + // + // presentation format has double quotes (") around the target. + // + return $this->priority . ' ' . $this->weight . ' "' . + $this->cleanString($this->target) . '"'; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->priority = $rdata[0]; + $this->weight = $rdata[1]; + + // + // make sure to trim the lead/trailing double quote if it's there. + // + $this->target = trim($this->cleanString($rdata[2]), '"'); + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // unpack the priority and weight + // + $x = unpack('npriority/nweight', $this->rdata); + + $this->priority = $x['priority']; + $this->weight = $x['weight']; + + $offset = $packet->offset + 4; + $this->target = Net_DNS2_Packet::expand($packet, $offset); + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->target) > 0) { + + $data = pack('nn', $this->priority, $this->weight); + $packet->offset += 4; + + $data .= $packet->compress(trim($this->target, '"'), $packet->offset); + + return $data; + } + + return null; + } +} + +?> diff --git a/include/pear/Net/DNS2/RR/WKS.php b/include/pear/Net/DNS2/RR/WKS.php new file mode 100644 index 0000000000000000000000000000000000000000..437eb52ed7c09708dad044d74bf810d2616d626c --- /dev/null +++ b/include/pear/Net/DNS2/RR/WKS.php @@ -0,0 +1,235 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: WKS.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 1.0.1 + * + */ + +/** + * WKS Resource Record - RFC1035 section 3.4.2 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ADDRESS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PROTOCOL | | + * +--+--+--+--+--+--+--+--+ | + * | | + * / <BIT MAP> / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_WKS extends Net_DNS2_RR +{ + /* + * The IP address of the service + */ + public $address; + + /* + * The protocol of the service + */ + public $protocol; + + /* + * bitmap + */ + public $bitmap = array(); + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + $data = $this->address . ' ' . $this->protocol; + + foreach ($this->bitmap as $port) { + $data .= ' ' . $port; + } + + return $data; + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $this->address = strtolower(trim(array_shift($rdata), '.')); + $this->protocol = array_shift($rdata); + $this->bitmap = $rdata; + + return true; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + // + // get the address and protocol value + // + $x = unpack('Naddress/Cprotocol', $this->rdata); + + $this->address = long2ip($x['address']); + $this->protocol = $x['protocol']; + + // + // unpack the port list bitmap + // + $port = 0; + foreach (unpack('@5/C*', $this->rdata) as $set) { + + $s = sprintf('%08b', $set); + + for ($i=0; $i<8; $i++, $port++) { + if ($s[$i] == '1') { + $this->bitmap[] = $port; + } + } + } + + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->address) > 0) { + + $data = pack('NC', ip2long($this->address), $this->protocol); + + $ports = array(); + + $n = 0; + foreach ($this->bitmap as $port) { + $ports[$port] = 1; + + if ($port > $n) { + $n = $port; + } + } + for ($i=0; $i<ceil($n/8)*8; $i++) { + if (!isset($ports[$i])) { + $ports[$i] = 0; + } + } + + ksort($ports); + + $string = ''; + $n = 0; + + foreach ($ports as $s) { + + $string .= $s; + $n++; + + if ($n == 8) { + + $data .= chr(bindec($string)); + $string = ''; + $n = 0; + } + } + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/RR/X25.php b/include/pear/Net/DNS2/RR/X25.php new file mode 100644 index 0000000000000000000000000000000000000000..b1e6cfde43bd5f0e5fa54d9387e3f22e0cf9e3ef --- /dev/null +++ b/include/pear/Net/DNS2/RR/X25.php @@ -0,0 +1,160 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: X25.php 179 2012-11-23 05:49:01Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * X25 Resource Record - RFC1183 section 3.1 + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / PSDN-address / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_RR + * + */ +class Net_DNS2_RR_X25 extends Net_DNS2_RR +{ + /* + * The PSDN address + */ + public $psdnaddress; + + /** + * method to return the rdata portion of the packet as a string + * + * @return string + * @access protected + * + */ + protected function rrToString() + { + return $this->formatString($this->psdnaddress); + } + + /** + * parses the rdata portion from a standard DNS config line + * + * @param array $rdata a string split line of values for the rdata + * + * @return boolean + * @access protected + * + */ + protected function rrFromString(array $rdata) + { + $data = $this->buildString($rdata); + if (count($data) == 1) { + + $this->psdnaddress = $data[0]; + return true; + } + + return false; + } + + /** + * parses the rdata of the Net_DNS2_Packet object + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from + * + * @return boolean + * @access protected + * + */ + protected function rrSet(Net_DNS2_Packet &$packet) + { + if ($this->rdlength > 0) { + + $this->psdnaddress = Net_DNS2_Packet::label($packet, $packet->offset); + return true; + } + + return false; + } + + /** + * returns the rdata portion of the DNS packet + * + * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for + * compressed names + * + * @return mixed either returns a binary packed + * string or null on failure + * @access protected + * + */ + protected function rrGet(Net_DNS2_Packet &$packet) + { + if (strlen($this->psdnaddress) > 0) { + + $data = chr(strlen($this->psdnaddress)) . $this->psdnaddress; + + $packet->offset += strlen($data); + + return $data; + } + + return null; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Resolver.php b/include/pear/Net/DNS2/Resolver.php new file mode 100644 index 0000000000000000000000000000000000000000..fdb99e04d25277d3f32a064ff8904ee1b9c506db --- /dev/null +++ b/include/pear/Net/DNS2/Resolver.php @@ -0,0 +1,332 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Resolver.php 191 2013-04-07 23:28:20Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * This is the main resolver class, providing DNS query functions. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2 + * + */ +class Net_DNS2_Resolver extends Net_DNS2 +{ + /** + * Constructor - creates a new Net_DNS2_Resolver object + * + * @param mixed $options either an array with options or null + * + * @access public + * + */ + public function __construct(array $options = null) + { + parent::__construct($options); + } + + /** + * does a basic DNS lookup query + * + * @param string $name the DNS name to loookup + * @param string $type the name of the RR type to lookup + * @param string $class the name of the RR class to lookup + * + * @return Net_DNS_RR object + * @throws Net_DNS2_Exception + * @access public + * + */ + public function query($name, $type = 'A', $class = 'IN') + { + // + // make sure we have some name servers set + // + $this->checkServers(Net_DNS2::RESOLV_CONF); + + // + // we dont' support incremental zone tranfers; so if it's requested, a full + // zone transfer can be returned + // + if ($type == 'IXFR') { + + $type = 'AXFR'; + } + + // + // if the name *looks* too short, then append the domain from the config + // + if ( (strpos($name, '.') === false) && ($type != 'PTR') ) { + + $name .= '.' . strtolower($this->domain); + } + + // + // create a new packet based on the input + // + $packet = new Net_DNS2_Packet_Request($name, $type, $class); + + // + // check for an authentication method; either TSIG or SIG + // + if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) + || ($this->auth_signature instanceof Net_DNS2_RR_SIG) + ) { + $packet->additional[] = $this->auth_signature; + $packet->header->arcount = count($packet->additional); + } + + // + // check for the DNSSEC flag, and if it's true, then add an OPT + // RR to the additional section, and set the DO flag to 1. + // + if ($this->dnssec == true) { + + // + // create a new OPT RR + // + $opt = new Net_DNS2_RR_OPT(); + + // + // set the DO flag, and the other values + // + $opt->do = 1; + $opt->class = $this->dnssec_payload_size; + + // + // add the RR to the additional section. + // + $packet->additional[] = $opt; + $packet->header->arcount = count($packet->additional); + } + + // + // set the DNSSEC AD or CD bits + // + if ($this->dnssec_ad_flag == true) { + + $packet->header->ad = 1; + } + if ($this->dnssec_cd_flag == true) { + + $packet->header->cd = 1; + } + + // + // if caching is turned on, then check then hash the question, and + // do a cache lookup. + // + // don't use the cache for zone transfers + // + $packet_hash = ''; + + if ( ($this->use_cache == true) && ($this->cacheable($type) == true) ) { + + // + // open the cache + // + $this->cache->open( + $this->cache_file, $this->cache_size, $this->cache_serializer + ); + + // + // build the key and check for it in the cache. + // + $packet_hash = md5( + $packet->question[0]->qname . '|' . $packet->question[0]->qtype + ); + + if ($this->cache->has($packet_hash)) { + + return $this->cache->get($packet_hash); + } + } + + // + // set the RD (recursion desired) bit to 1 / 0 depending on the config + // setting. + // + if ($this->recurse == false) { + $packet->header->rd = 0; + } else { + $packet->header->rd = 1; + } + + // + // send the packet and get back the response + // + // *always* use TCP for zone transfers- does this cause any problems? + // + $response = $this->sendPacket( + $packet, ($type == 'AXFR') ? true : $this->use_tcp + ); + + // + // if strict mode is enabled, then make sure that the name that was + // looked up is *actually* in the response object. + // + // only do this is strict_query_mode is turned on, AND we've received + // some answers; no point doing any else if there were no answers. + // + if ( ($this->strict_query_mode == true) + && ($response->header->ancount > 0) + ) { + + $found = false; + + // + // look for the requested name/type/class + // + foreach ($response->answer as $index => $object) { + + if ( (strcasecmp($object->name, $name) == 0) + && ($object->type == $type) + && ($object->class == $class) + ) { + $found = true; + break; + } + } + + // + // if it's not found, then unset the answer section; it's not correct to + // throw an exception here; if the hostname didn't exist, then + // sendPacket() would have already thrown an NXDOMAIN error- so the host + // *exists*, but just not the request type/class. + // + // the correct response in this case, is an empty answer section; the + // authority section may still have usual information, like a SOA record. + // + if ($found == false) { + + $response->answer = array(); + $response->header->ancount = 0; + } + } + + // + // cache the response object + // + if ( ($this->use_cache == true) && ($this->cacheable($type) == true) ) { + + $this->cache->put($packet_hash, $response); + } + + return $response; + } + + /** + * does an inverse query for the given RR; most DNS servers do not implement + * inverse queries, but they should be able to return "not implemented" + * + * @param Net_DNS2_RR $rr the RR object to lookup + * + * @return Net_DNS_RR object + * @throws Net_DNS2_Exception + * @access public + * + */ + public function iquery(Net_DNS2_RR $rr) + { + // + // make sure we have some name servers set + // + $this->checkServers(Net_DNS2::RESOLV_CONF); + + // + // create an empty packet + // + $packet = new Net_DNS2_Packet_Request($rr->name, 'A', 'IN'); + + // + // unset the question + // + $packet->question = array(); + $packet->header->qdcount = 0; + + // + // set the opcode to IQUERY + // + $packet->header->opcode = Net_DNS2_Lookups::OPCODE_IQUERY; + + // + // add the given RR as the answer + // + $packet->answer[] = $rr; + $packet->header->ancount = 1; + + // + // check for an authentication method; either TSIG or SIG + // + if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) + || ($this->auth_signature instanceof Net_DNS2_RR_SIG) + ) { + $packet->additional[] = $this->auth_signature; + $packet->header->arcount = count($packet->additional); + } + + // + // send the packet and get back the response + // + return $this->sendPacket($packet, $this->use_tcp); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Socket.php b/include/pear/Net/DNS2/Socket.php new file mode 100644 index 0000000000000000000000000000000000000000..090d2cbee45496478ee5aa31a9378eeb6878c585 --- /dev/null +++ b/include/pear/Net/DNS2/Socket.php @@ -0,0 +1,189 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Socket.php 176 2012-11-16 02:14:09Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/* + * check to see if the socket defines exist; if they don't, then define them + */ +if (defined('SOCK_STREAM') == false) { + define('SOCK_STREAM', 1); +} +if (defined('SOCK_DGRAM') == false) { + define('SOCK_DGRAM', 2); +} + +/** + * This is the abstract base class for the two sockets classes; this simply + * provides the class definition for the two sockets classes. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Socket_Sockets, Net_DNS2_Socket_Streams + * + */ +abstract class Net_DNS2_Socket +{ + protected $sock; + protected $type; + protected $host; + protected $port; + protected $timeout; + + protected $local_host; + protected $local_port; + + public $last_error; + + /* + * type of sockets + */ + const SOCK_STREAM = SOCK_STREAM; + const SOCK_DGRAM = SOCK_DGRAM; + + /** + * constructor - set the port details + * + * @param integer $type the socket type + * @param string $host the IP address of the DNS server to connect to + * @param integer $port the port of the DNS server to connect to + * @param integer $timeout the timeout value to use for socket functions + * + * @access public + * + */ + public function __construct($type, $host, $port, $timeout) + { + $this->type = $type; + $this->host = $host; + $this->port = $port; + $this->timeout = $timeout; + } + + /** + * destructor + * + * @access public + */ + public function __destruct() + { + $this->close(); + } + + /** + * sets the local address/port for the socket to bind to + * + * @param string $address the local IP address to bind to + * @param mixed $port the local port to bind to, or 0 to let the socket + * function select a port + * + * @return boolean + * @access public + * + */ + public function bindAddress($address, $port = 0) + { + $this->local_host = $address; + $this->local_port = $port; + + return true; + } + + /** + * opens a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + abstract public function open(); + + /** + * closes a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + abstract public function close(); + + /** + * writes the given string to the DNS server socket + * + * @param string $data a binary packed DNS packet + * + * @return boolean + * @access public + * + */ + abstract public function write($data); + + /** + * reads a response from a DNS server + * + * @param integer &$size the size of the DNS packet read is passed back + * + * @return mixed returns the data on success and false on error + * @access public + * + */ + abstract public function read(&$size); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Socket/Sockets.php b/include/pear/Net/DNS2/Socket/Sockets.php new file mode 100644 index 0000000000000000000000000000000000000000..e17c73a9ae88aa9bf4e87433aefa33d05fd36243 --- /dev/null +++ b/include/pear/Net/DNS2/Socket/Sockets.php @@ -0,0 +1,370 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Sockets.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * Socket handling class using the PHP sockets extension + * + * The sockets extension is faster than the stream functions in PHP, but it's + * not standard. So if the extension is loaded, then this class is used, if + * it's not, then the Net_DNS2_Socket_Streams class is used. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Socket + * + */ +class Net_DNS2_Socket_Sockets extends Net_DNS2_Socket +{ + /** + * opens a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + public function open() + { + // + // create the socket + // + if (Net_DNS2::isIPv4($this->host) == true) { + + $this->sock = @socket_create( + AF_INET, $this->type, + ($this->type == Net_DNS2_Socket::SOCK_STREAM) ? SOL_TCP : SOL_UDP + ); + + } else if (Net_DNS2::isIPv6($this->host) == true) { + + $this->sock = @socket_create( + AF_INET6, $this->type, + ($this->type == Net_DNS2_Socket::SOCK_STREAM) ? SOL_TCP : SOL_UDP + ); + + } else { + + $this->last_error = 'invalid address type: ' . $this->host; + return false; + } + + if ($this->sock === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + @socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1); + + // + // bind to a local IP/port if it's set + // + if (strlen($this->local_host) > 0) { + + $result = @socket_bind( + $this->sock, $this->local_host, + ($this->local_port > 0) ? $this->local_port : null + ); + if ($result === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + } + + // + // mark the socket as non-blocking + // + if (@socket_set_nonblock($this->sock) === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + // + // connect to the socket; don't check for status here, we'll check it on the + // socket_select() call so we can handle timeouts properly + // + @socket_connect($this->sock, $this->host, $this->port); + + $read = null; + $write = array($this->sock); + $except = null; + + // + // select on write to check if the call to connect worked + // + switch(@socket_select($read, $write, $except, $this->timeout)) { + case false: + $this->last_error = socket_strerror(socket_last_error()); + return false; + break; + + case 0: + return false; + break; + + default: + ; + } + + return true; + } + + /** + * closes a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + public function close() + { + if (is_resource($this->sock) === true) { + + @socket_close($this->sock); + } + return true; + } + + /** + * writes the given string to the DNS server socket + * + * @param string $data a binary packed DNS packet + * + * @return boolean + * @access public + * + */ + public function write($data) + { + $length = strlen($data); + if ($length == 0) { + + $this->last_error = 'empty data on write()'; + return false; + } + + $read = null; + $write = array($this->sock); + $except = null; + + // + // select on write + // + switch(@socket_select($read, $write, $except, $this->timeout)) { + case false: + $this->last_error = socket_strerror(socket_last_error()); + return false; + break; + + case 0: + return false; + break; + + default: + ; + } + + // + // if it's a TCP socket, then we need to packet and send the length of the + // data as the first 16bit of data. + // + if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { + + $s = chr($length >> 8) . chr($length); + + if (@socket_write($this->sock, $s) === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + } + + // + // write the data to the socket + // + $size = @socket_write($this->sock, $data); + if ( ($size === false) || ($size != $length) ) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + return true; + } + + /** + * reads a response from a DNS server + * + * @param integer &$size the size of the DNS packet read is passed back + * + * @return mixed returns the data on success and false on error + * @access public + * + */ + public function read(&$size) + { + $read = array($this->sock); + $write = null; + $except = null; + + // + // make sure our socket is non-blocking + // + if (@socket_set_nonblock($this->sock) === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + // + // select on read + // + switch(@socket_select($read, $write, $except, $this->timeout)) { + case false: + $this->last_error = socket_strerror(socket_last_error()); + return false; + break; + + case 0: + return false; + break; + + default: + ; + } + + $data = ''; + $length = Net_DNS2_Lookups::DNS_MAX_UDP_SIZE; + + // + // if it's a TCP socket, then the first two bytes is the length of the DNS + // packet- we need to read that off first, then use that value for the + // packet read. + // + if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { + + if (($size = @socket_recv($this->sock, $data, 2, 0)) === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + $length = ord($data[0]) << 8 | ord($data[1]); + if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) { + + return false; + } + } + + // + // at this point, we know that there is data on the socket to be read, + // because we've already extracted the length from the first two bytes. + // + // so the easiest thing to do, is just turn off socket blocking, and + // wait for the data. + // + if (@socket_set_block($this->sock) === false) { + + $this->last_error = socket_strerror(socket_last_error()); + return false; + } + + // + // read the data from the socket + // + // loop while reading since some OS's (specifically Win < 2003) don't support + // MSG_WAITALL properly, so they may return with less data than is available. + // + // According to M$, XP and below don't support MSG_WAITALL at all; and there + // also seems to be some issue in 2003 and 2008 where the MSG_WAITALL is + // defined as 0, but if you actually pass 8 (which is the correct defined + // value), it works as it's supposed to- so in these cases, it's just the + // define that's incorrect- this is likely a PHP issue. + // + $data = ''; + $size = 0; + + while (1) { + + $chunk_size = @socket_recv($this->sock, $chunk, $length, MSG_WAITALL); + if ($chunk_size === false) { + + $size = $chunk_size; + $this->last_error = socket_strerror(socket_last_error()); + + return false; + } + + $data .= $chunk; + $size += $chunk_size; + + $length -= $chunk_size; + if ( ($length <= 0) || ($this->type == Net_DNS2_Socket::SOCK_DGRAM) ) { + break; + } + } + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Socket/Streams.php b/include/pear/Net/DNS2/Socket/Streams.php new file mode 100644 index 0000000000000000000000000000000000000000..08a09704a9a940d13b4c6b605af4bf1f0608e687 --- /dev/null +++ b/include/pear/Net/DNS2/Socket/Streams.php @@ -0,0 +1,393 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Streams.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * Socket handling class using the PHP Streams + * + * The sockets extension is faster than the stream functions in PHP, but it's + * not standard. So if the extension is loaded, then the Net_DNS_Socket_Sockets + * class it used, otherwise, this class it used. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2_Socket + * + */ +class Net_DNS2_Socket_Streams extends Net_DNS2_Socket +{ + private $_context; + + /** + * opens a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + public function open() + { + // + // create a list of options for the context + // + $opts = array('socket' => array()); + + // + // bind to a local IP/port if it's set + // + if (strlen($this->local_host) > 0) { + + $opts['socket']['bindto'] = $this->local_host; + if ($this->local_port > 0) { + + $opts['socket']['bindto'] .= ':' . $this->local_port; + } + } + + // + // create the context + // + $this->_context = @stream_context_create($opts); + + // + // create socket + // + $errno; + $errstr; + + switch($this->type) { + case Net_DNS2_Socket::SOCK_STREAM: + + if (Net_DNS2::isIPv4($this->host) == true) { + + $this->sock = @stream_socket_client( + 'tcp://' . $this->host . ':' . $this->port, + $errno, $errstr, $this->timeout, + STREAM_CLIENT_CONNECT, $this->_context + ); + } else if (Net_DNS2::isIPv6($this->host) == true) { + + $this->sock = @stream_socket_client( + 'tcp://[' . $this->host . ']:' . $this->port, + $errno, $errstr, $this->timeout, + STREAM_CLIENT_CONNECT, $this->_context + ); + } else { + + $this->last_error = 'invalid address type: ' . $this->host; + return false; + } + + break; + + case Net_DNS2_Socket::SOCK_DGRAM: + + if (Net_DNS2::isIPv4($this->host) == true) { + + $this->sock = @stream_socket_client( + 'udp://' . $this->host . ':' . $this->port, + $errno, $errstr, $this->timeout, + STREAM_CLIENT_CONNECT, $this->_context + ); + } else if (Net_DNS2::isIPv6($this->host) == true) { + + $this->sock = @stream_socket_client( + 'udp://[' . $this->host . ']:' . $this->port, + $errno, $errstr, $this->timeout, + STREAM_CLIENT_CONNECT, $this->_context + ); + } else { + + $this->last_error = 'invalid address type: ' . $this->host; + return false; + } + + break; + + default: + $this->last_error = 'Invalid socket type: ' . $this->type; + return false; + } + + if ($this->sock === false) { + + $this->last_error = $errstr; + return false; + } + + // + // set it to non-blocking and set the timeout + // + @stream_set_blocking($this->sock, 0); + @stream_set_timeout($this->sock, $this->timeout); + + return true; + } + + /** + * closes a socket connection to the DNS server + * + * @return boolean + * @access public + * + */ + public function close() + { + if (is_resource($this->sock) === true) { + + @fclose($this->sock); + } + return true; + } + + /** + * writes the given string to the DNS server socket + * + * @param string $data a binary packed DNS packet + * + * @return boolean + * @access public + * + */ + public function write($data) + { + $length = strlen($data); + if ($length == 0) { + + $this->last_error = 'empty data on write()'; + return false; + } + + $read = null; + $write = array($this->sock); + $except = null; + + // + // select on write + // + switch(@stream_select($read, $write, $except, $this->timeout)) { + case false: + $this->last_error = 'failed on stream_select()'; + return false; + break; + + case 0: + return false; + break; + + default: + ; + } + + // + // if it's a TCP socket, then we need to packet and send the length of the + // data as the first 16bit of data. + // + if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { + + $s = chr($length >> 8) . chr($length); + + if (@fwrite($this->sock, $s) === false) { + + $this->last_error = 'failed to fwrite() 16bit length'; + return false; + } + } + + // + // write the data to the socket + // + $size = @fwrite($this->sock, $data); + if ( ($size === false) || ($size != $length) ) { + + $this->last_error = 'failed to fwrite() packet'; + return false; + } + + return true; + } + + /** + * reads a response from a DNS server + * + * @param integer &$size the size of the DNS packet read is passed back + * + * @return mixed returns the data on success and false on error + * @access public + * + */ + public function read(&$size) + { + $read = array($this->sock); + $write = null; + $except = null; + + // + // make sure our socket is non-blocking + // + @stream_set_blocking($this->sock, 0); + + // + // select on read + // + switch(stream_select($read, $write, $except, $this->timeout)) { + case false: + $this->last_error = 'error on stream_select()'; + return false; + break; + + case 0: + return false; + break; + + default: + ; + } + + $data = ''; + $length = Net_DNS2_Lookups::DNS_MAX_UDP_SIZE; + + // + // if it's a TCP socket, then the first two bytes is the length of the DNS + // packet- we need to read that off first, then use that value for the + // packet read. + // + if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { + + if (($data = fread($this->sock, 2)) === false) { + + $this->last_error = 'failed on fread() for data length'; + return false; + } + + $length = ord($data[0]) << 8 | ord($data[1]); + if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) { + + return false; + } + } + + // + // at this point, we know that there is data on the socket to be read, + // because we've already extracted the length from the first two bytes. + // + // so the easiest thing to do, is just turn off socket blocking, and + // wait for the data. + // + @stream_set_blocking($this->sock, 1); + + // + // read the data from the socket + // + $data = ''; + + // + // the streams socket is weird for TCP sockets; it doesn't seem to always + // return all the data properly; but the looping code I added broke UDP + // packets- my fault- + // + // the sockets library works much better. + // + if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { + + $chunk = ''; + $chunk_size = $length; + + // + // loop so we make sure we read all the data + // + while (1) { + + $chunk = fread($this->sock, $chunk_size); + if ($chunk === false) { + + $this->last_error = 'failed on fread() for data'; + return false; + } + + $data .= $chunk; + $chunk_size -= strlen($chunk); + + if (strlen($data) >= $length) { + break; + } + } + + } else { + + // + // if it's UDP, it's a single fixed-size frame, and the streams library + // doesn't seem to have a problem reading it. + // + $data = fread($this->sock, $length); + if ($length === false) { + + $this->last_error = 'failed on fread() for data'; + return false; + } + } + + $size = strlen($data); + + return $data; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/pear/Net/DNS2/Updater.php b/include/pear/Net/DNS2/Updater.php new file mode 100644 index 0000000000000000000000000000000000000000..e3446a522d4e7cc7052104d0a5a5bfbee7b90ea1 --- /dev/null +++ b/include/pear/Net/DNS2/Updater.php @@ -0,0 +1,654 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * DNS Library for handling lookups and updates. + * + * PHP Version 5 + * + * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Mike Pultz nor the names of his contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @copyright 2010 Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version SVN: $Id: Updater.php 198 2013-05-26 05:05:22Z mike.pultz $ + * @link http://pear.php.net/package/Net_DNS2 + * @since File available since Release 0.6.0 + * + */ + +/** + * The main dynamic DNS updater class. + * + * This class provices functions to handle all defined dynamic DNS update + * requests as defined by RFC 2136. + * + * This is separate from the Net_DNS2_Resolver class, as while the underlying + * protocol is the same, the functionality is completely different. + * + * Generally, query (recursive) lookups are done against caching server, while + * update requests are done against authoratative servers. + * + * @category Networking + * @package Net_DNS2 + * @author Mike Pultz <mike@mikepultz.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DNS2 + * @see Net_DNS2 + * + */ +class Net_DNS2_Updater extends Net_DNS2 +{ + /* + * a Net_DNS2_Packet_Request object used for the update request + */ + private $_packet; + + /** + * Constructor - builds a new Net_DNS2_Updater objected used for doing + * dynamic DNS updates + * + * @param string $zone the domain name to use for DNS updates + * @param mixed $options an array of config options or null + * + * @throws Net_DNS2_Exception + * @access public + * + */ + public function __construct($zone, array $options = null) + { + parent::__construct($options); + + // + // create the packet + // + $this->_packet = new Net_DNS2_Packet_Request( + strtolower(trim($zone, " \n\r\t.")), 'SOA', 'IN' + ); + + // + // make sure the opcode on the packet is set to UPDATE + // + $this->_packet->header->opcode = Net_DNS2_Lookups::OPCODE_UPDATE; + } + + /** + * checks that the given name matches the name for the zone we're updating + * + * @param string $name The name to be checked. + * + * @return boolean + * @throws Net_DNS2_Exception + * @access private + * + */ + private function _checkName($name) + { + if (!preg_match('/' . $this->_packet->question[0]->qname . '$/', $name)) { + + throw new Net_DNS2_Exception( + 'name provided (' . $name . ') does not match zone name (' . + $this->_packet->question[0]->qname . ')', + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + + return true; + } + + /** + * add a signature to the request for authentication + * + * @param string $keyname the key name to use for the TSIG RR + * @param string $signature the key to sign the request. + * + * @return boolean + * @access public + * @see Net_DNS2::signTSIG() + * @deprecated function deprecated in 1.1.0 + * + */ + public function signature($keyname, $signature) + { + return $this->signTSIG($keyname, $signature); + } + + /** + * 2.5.1 - Add To An RRset + * + * RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH + * and RDATA are those being added, and CLASS is the same as the zone + * class. Any duplicate RRs will be silently ignored by the primary + * master. + * + * @param Net_DNS2_RR $rr the Net_DNS2_RR object to be added to the zone + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function add(Net_DNS2_RR $rr) + { + $this->_checkName($rr->name); + + // + // add the RR to the "update" section + // + if (!in_array($rr, $this->_packet->authority)) { + $this->_packet->authority[] = $rr; + } + + return true; + } + + /** + * 2.5.4 - Delete An RR From An RRset + * + * RRs to be deleted are added to the Update Section. The NAME, TYPE, + * RDLENGTH and RDATA must match the RR being deleted. TTL must be + * specified as zero (0) and will otherwise be ignored by the primary + * master. CLASS must be specified as NONE to distinguish this from an + * RR addition. If no such RRs exist, then this Update RR will be + * silently ignored by the primary master. + * + * @param Net_DNS2_RR $rr the Net_DNS2_RR object to be deleted from the zone + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function delete(Net_DNS2_RR $rr) + { + $this->_checkName($rr->name); + + $rr->ttl = 0; + $rr->class = 'NONE'; + + // + // add the RR to the "update" section + // + if (!in_array($rr, $this->_packet->authority)) { + $this->_packet->authority[] = $rr; + } + + return true; + } + + /** + * 2.5.2 - Delete An RRset + * + * One RR is added to the Update Section whose NAME and TYPE are those + * of the RRset to be deleted. TTL must be specified as zero (0) and is + * otherwise not used by the primary master. CLASS must be specified as + * ANY. RDLENGTH must be zero (0) and RDATA must therefore be empty. + * If no such RRset exists, then this Update RR will be silently ignored + * by the primary master + * + * @param string $name the RR name to be removed from the zone + * @param string $type the RR type to be removed from the zone + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function deleteAny($name, $type) + { + $this->_checkName($name); + + $class = Net_DNS2_Lookups::$rr_types_id_to_class[ + Net_DNS2_Lookups::$rr_types_by_name[$type] + ]; + if (!isset($class)) { + + throw new Net_DNS2_Exception( + 'unknown or un-supported resource record type: ' . $type, + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + $rr = new $class; + + $rr->name = $name; + $rr->ttl = 0; + $rr->class = 'ANY'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "update" section + // + if (!in_array($rr, $this->_packet->authority)) { + $this->_packet->authority[] = $rr; + } + + return true; + } + + /** + * 2.5.3 - Delete All RRsets From A Name + * + * One RR is added to the Update Section whose NAME is that of the name + * to be cleansed of RRsets. TYPE must be specified as ANY. TTL must + * be specified as zero (0) and is otherwise not used by the primary + * master. CLASS must be specified as ANY. RDLENGTH must be zero (0) + * and RDATA must therefore be empty. If no such RRsets exist, then + * this Update RR will be silently ignored by the primary master. + * + * @param string $name the RR name to be removed from the zone + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function deleteAll($name) + { + $this->_checkName($name); + + // + // the Net_DNS2_RR_ANY class is just an empty stub class used for these + // cases only + // + $rr = new Net_DNS2_RR_ANY; + + $rr->name = $name; + $rr->ttl = 0; + $rr->type = 'ANY'; + $rr->class = 'ANY'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "update" section + // + if (!in_array($rr, $this->_packet->authority)) { + $this->_packet->authority[] = $rr; + } + + return true; + } + + /** + * 2.4.1 - RRset Exists (Value Independent) + * + * At least one RR with a specified NAME and TYPE (in the zone and class + * specified in the Zone Section) must exist. + * + * For this prerequisite, a requestor adds to the section a single RR + * whose NAME and TYPE are equal to that of the zone RRset whose + * existence is required. RDLENGTH is zero and RDATA is therefore + * empty. CLASS must be specified as ANY to differentiate this + * condition from that of an actual RR whose RDLENGTH is naturally zero + * (0) (e.g., NULL). TTL is specified as zero (0). + * + * @param string $name the RR name for the prerequisite + * @param string $type the RR type for the prerequisite + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function checkExists($name, $type) + { + $this->_checkName($name); + + $class = Net_DNS2_Lookups::$rr_types_id_to_class[ + Net_DNS2_Lookups::$rr_types_by_name[$type] + ]; + if (!isset($class)) { + + throw new Net_DNS2_Exception( + 'unknown or un-supported resource record type: ' . $type, + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + $rr = new $class; + + $rr->name = $name; + $rr->ttl = 0; + $rr->class = 'ANY'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "prerequisite" section + // + if (!in_array($rr, $this->_packet->answer)) { + $this->_packet->answer[] = $rr; + } + + return true; + } + + /** + * 2.4.2 - RRset Exists (Value Dependent) + * + * A set of RRs with a specified NAME and TYPE exists and has the same + * members with the same RDATAs as the RRset specified here in this + * section. While RRset ordering is undefined and therefore not + * significant to this comparison, the sets be identical in their + * extent. + * + * For this prerequisite, a requestor adds to the section an entire + * RRset whose preexistence is required. NAME and TYPE are that of the + * RRset being denoted. CLASS is that of the zone. TTL must be + * specified as zero (0) and is ignored when comparing RRsets for + * identity. + * + * @param Net_DNS2_RR $rr the RR object to be used as a prerequisite + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function checkValueExists(Net_DNS2_RR $rr) + { + $this->_checkName($rr->name); + + $rr->ttl = 0; + + // + // add the RR to the "prerequisite" section + // + if (!in_array($rr, $this->_packet->answer)) { + $this->_packet->answer[] = $rr; + } + + return true; + } + + /** + * 2.4.3 - RRset Does Not Exist + * + * No RRs with a specified NAME and TYPE (in the zone and class denoted + * by the Zone Section) can exist. + * + * For this prerequisite, a requestor adds to the section a single RR + * whose NAME and TYPE are equal to that of the RRset whose nonexistence + * is required. The RDLENGTH of this record is zero (0), and RDATA + * field is therefore empty. CLASS must be specified as NONE in order + * to distinguish this condition from a valid RR whose RDLENGTH is + * naturally zero (0) (for example, the NULL RR). TTL must be specified + * as zero (0). + * + * @param string $name the RR name for the prerequisite + * @param string $type the RR type for the prerequisite + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function checkNotExists($name, $type) + { + $this->_checkName($name); + + $class = Net_DNS2_Lookups::$rr_types_id_to_class[ + Net_DNS2_Lookups::$rr_types_by_name[$type] + ]; + if (!isset($class)) { + + throw new Net_DNS2_Exception( + 'unknown or un-supported resource record type: ' . $type, + Net_DNS2_Lookups::E_RR_INVALID + ); + } + + $rr = new $class; + + $rr->name = $name; + $rr->ttl = 0; + $rr->class = 'NONE'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "prerequisite" section + // + if (!in_array($rr, $this->_packet->answer)) { + $this->_packet->answer[] = $rr; + } + + return true; + } + + /** + * 2.4.4 - Name Is In Use + * + * Name is in use. At least one RR with a specified NAME (in the zone + * and class specified by the Zone Section) must exist. Note that this + * prerequisite is NOT satisfied by empty nonterminals. + * + * For this prerequisite, a requestor adds to the section a single RR + * whose NAME is equal to that of the name whose ownership of an RR is + * required. RDLENGTH is zero and RDATA is therefore empty. CLASS must + * be specified as ANY to differentiate this condition from that of an + * actual RR whose RDLENGTH is naturally zero (0) (e.g., NULL). TYPE + * must be specified as ANY to differentiate this case from that of an + * RRset existence test. TTL is specified as zero (0). + * + * @param string $name the RR name for the prerequisite + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function checkNameInUse($name) + { + $this->_checkName($name); + + // + // the Net_DNS2_RR_ANY class is just an empty stub class used for these + // cases only + // + $rr = new Net_DNS2_RR_ANY; + + $rr->name = $name; + $rr->ttl = 0; + $rr->type = 'ANY'; + $rr->class = 'ANY'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "prerequisite" section + // + if (!in_array($rr, $this->_packet->answer)) { + $this->_packet->answer[] = $rr; + } + + return true; + } + + /** + * 2.4.5 - Name Is Not In Use + * + * Name is not in use. No RR of any type is owned by a specified NAME. + * Note that this prerequisite IS satisfied by empty nonterminals. + * + * For this prerequisite, a requestor adds to the section a single RR + * whose NAME is equal to that of the name whose nonownership of any RRs + * is required. RDLENGTH is zero and RDATA is therefore empty. CLASS + * must be specified as NONE. TYPE must be specified as ANY. TTL must + * be specified as zero (0). + * + * @param string $name the RR name for the prerequisite + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function checkNameNotInUse($name) + { + $this->_checkName($name); + + // + // the Net_DNS2_RR_ANY class is just an empty stub class used for these + // cases only + // + $rr = new Net_DNS2_RR_ANY; + + $rr->name = $name; + $rr->ttl = 0; + $rr->type = 'ANY'; + $rr->class = 'NONE'; + $rr->rdlength = -1; + $rr->rdata = ''; + + // + // add the RR to the "prerequisite" section + // + if (!in_array($rr, $this->_packet->answer)) { + $this->_packet->answer[] = $rr; + } + + return true; + } + + /** + * returns the current internal packet object. + * + * @return Net_DNS2_Packet_Request + * @access public + # + */ + public function packet() + { + // + // take a copy + // + $p = $this->_packet; + + // + // check for an authentication method; either TSIG or SIG + // + if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) + || ($this->auth_signature instanceof Net_DNS2_RR_SIG) + ) { + $p->additional[] = $this->auth_signature; + } + + // + // update the counts + // + $p->header->qdcount = count($p->question); + $p->header->ancount = count($p->answer); + $p->header->nscount = count($p->authority); + $p->header->arcount = count($p->additional); + + return $p; + } + + /** + * executes the update request with the object informaton + * + * @param Net_DNS2_Packet_Response &$response ref to the response object + * + * @return boolean + * @throws Net_DNS2_Exception + * @access public + * + */ + public function update(&$response = null) + { + // + // make sure we have some name servers set + // + $this->checkServers(Net_DNS2::RESOLV_CONF); + + // + // check for an authentication method; either TSIG or SIG + // + if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) + || ($this->auth_signature instanceof Net_DNS2_RR_SIG) + ) { + $this->_packet->additional[] = $this->auth_signature; + } + + // + // update the counts + // + $this->_packet->header->qdcount = count($this->_packet->question); + $this->_packet->header->ancount = count($this->_packet->answer); + $this->_packet->header->nscount = count($this->_packet->authority); + $this->_packet->header->arcount = count($this->_packet->additional); + + // + // make sure we have some data to send + // + if ( ($this->_packet->header->qdcount == 0) + || ($this->_packet->header->nscount == 0) + ) { + throw new Net_DNS2_Exception( + 'empty headers- nothing to send!', + Net_DNS2_Lookups::E_PACKET_INVALID + ); + } + + // + // send the packet and get back the response + // + $response = $this->sendPacket($this->_packet, $this->use_tcp); + + // + // clear the internal packet so if we make another request, we don't have + // old data being sent. + // + $this->_packet->reset(); + + // + // for updates, we just need to know it worked- we don't actualy need to + // return the response object + // + return true; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/include/plugins/.keep b/include/plugins/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/include/staff/cannedresponse.inc.php b/include/staff/cannedresponse.inc.php index d82baad74ffb9a98cd6dc494f3ab4ce9ae55c8fb..a14beb43a983548997e2e02945bc410d246d709e 100644 --- a/include/staff/cannedresponse.inc.php +++ b/include/staff/cannedresponse.inc.php @@ -87,7 +87,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); if($canned && ($files=$canned->attachments->getSeparates())) { echo '<div id="canned_attachments"><span class="faded">Uncheck to delete the attachment on submit</span><br>'; foreach($files as $file) { - $hash=$file['hash'].md5($file['id'].session_id().$file['hash']); + $hash=$file['key'].md5($file['id'].session_id().strtolower($file['key'])); echo sprintf('<label><input type="checkbox" name="files[]" id="f%d" value="%d" checked="checked"> <a href="file.php?h=%s">%s</a> </label> ', $file['id'], $file['id'], $hash, $file['name']); diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php index c452cab26d3074540e292dda715b3b206fd53cda..2a001a4037d5864849f6e5b718227f7270c5648c 100644 --- a/include/staff/department.inc.php +++ b/include/staff/department.inc.php @@ -86,7 +86,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <select name="tpl_id"> - <option value="0">— System default —</option> + <option value="0">— System Default —</option> <?php $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_GRP_TABLE.' tpl WHERE isactive=1 ORDER by name'; if(($res=db_query($sql)) && db_num_rows($res)){ @@ -106,7 +106,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <select name="sla_id"> - <option value="0">— System default —</option> + <option value="0">— System Default —</option> <?php if($slas=SLA::getSLAs()) { foreach($slas as $id =>$name) { @@ -176,12 +176,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <tr> <td width="180"> - Auto Response Email: + Auto-Response Email: </td> <td> <select name="autoresp_email_id"> - <option value="" disabled="disabled">Select Outgoing Email</option> - <option value="0">— Department Email (Above) —</option> + <option value="0" selected="selected">— Department Email —</option> <?php $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' email ORDER by name'; if(($res=db_query($sql)) && db_num_rows($res)){ diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php index fffe5d9b2ad549a8ebe34deb9c57167bac7dd04a..4beb705768cf298e1d62255a4bdc16334066642b 100644 --- a/include/staff/dynamic-list.inc.php +++ b/include/staff/dynamic-list.inc.php @@ -1,7 +1,7 @@ <?php $info=array(); -if($list && $_REQUEST['a']!='add') { +if($list && !$errors) { $title = 'Update custom list'; $action = 'update'; $submit_text='Save Changes'; @@ -19,6 +19,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <form action="?" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="a" value="<?php echo $_REQUEST['a']; ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2>Custom List</h2> <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> @@ -33,7 +34,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tbody> <tr> <td width="180" class="required">Name:</td> - <td><input size="50" type="text" name="name" value="<?php echo $info['name']; ?>"/></td> + <td><input size="50" type="text" name="name" value="<?php echo $info['name']; ?>"/> + <span class="error">*<br/><?php echo $errors['name']; ?></td> </tr> <tr> <td width="180">Plural Name:</td> diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php index 33ddc72c14f6adf9bed9a3c37b99a07d51c26d8a..193a5347815237a41b2ffbc613cbb0a75b05914f 100644 --- a/include/staff/email.inc.php +++ b/include/staff/email.inc.php @@ -69,9 +69,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <select name="priority_id"> - <option value="">— Select Priority —</option> + <option value="0" selected="selected">— System Default —</option> <?php - $sql='SELECT priority_id,priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC'; + $sql='SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC'; if(($res=db_query($sql)) && db_num_rows($res)){ while(list($id,$name)=db_fetch_row($res)){ $selected=($info['priority_id'] && $id==$info['priority_id'])?'selected="selected"':''; @@ -89,9 +89,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> <td> <select name="dept_id"> - <option value="">— Select Department —</option> + <option value="0" selected="selected">— System Default —</option> <?php - $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name'; + $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name'; if(($res=db_query($sql)) && db_num_rows($res)){ while(list($id,$name)=db_fetch_row($res)){ $selected=($info['dept_id'] && $id==$info['dept_id'])?'selected="selected"':''; diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php index 8b390c3de8d43762ce79f5560d07486e2c8bc636..8f412bdb69d27a4461dad20cd1f43aebe14b723a 100644 --- a/include/staff/faq.inc.php +++ b/include/staff/faq.inc.php @@ -101,7 +101,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); if($faq && ($files=$faq->attachments->getSeparates())) { echo '<div class="faq_attachments"><span class="faded">Uncheck to delete the attachment on submit</span><br>'; foreach($files as $file) { - $hash=$file['hash'].md5($file['id'].session_id().$file['hash']); + $hash=$file['key'].md5($file['id'].session_id().strtolower($file['key'])); echo sprintf('<label><input type="checkbox" name="files[]" id="f%d" value="%d" checked="checked"> <a href="file.php?h=%s">%s</a> </label> ', $file['id'], $file['id'], $hash, $file['name']); diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index 37ac6c69925bf4dc15711f0a76767803aeacc66a..74e72106859d68501fbc6e490d79a9ddf90a4869 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -121,7 +121,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); for($i=1; $i<=$n; $i++){ ?> <tr id="r<?php echo $i; ?>"> <td colspan="2"> - <div style="width:700px; float:left;"> + <div> <select name="rule_w<?php echo $i; ?>"> <option value="">— Select One ‐</option> <?php @@ -143,15 +143,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); } ?> </select> - <input type="text" size="30" name="rule_v<?php echo $i; ?>" value="<?php echo $info["rule_v$i"]; ?>"> + <input type="text" size="60" name="rule_v<?php echo $i; ?>" value="<?php echo $info["rule_v$i"]; ?>"> <span class="error"> <?php echo $errors["rule_$i"]; ?></span> - </div> <?php if($info["rule_w$i"] || $info["rule_h$i"] || $info["rule_v$i"]){ ?> <div style="float:right;text-align:right;padding-right:20px;"><a href="#" class="clearrule">(clear)</a></div> <?php } ?> - <div class="clear"></div> + </div> </td> </tr> <?php diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php index aa4e2f1130aae8899778d30ff16f92bb1ce27dad..5cb94ea3d817be39355871fb783369dedd009cb9 100644 --- a/include/staff/footer.inc.php +++ b/include/staff/footer.inc.php @@ -1,6 +1,6 @@ </div> <div id="footer"> - Copyright © 2006-<?php echo date('Y'); ?> osTicket.com. All Rights Reserved. + Copyright © 2006-<?php echo date('Y'); ?> <?php echo (string) $ost->company ?: 'osTicket.com'; ?> All Rights Reserved. </div> <?php if(is_object($thisstaff) && $thisstaff->isStaff()) { ?> diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php index 1f3bb55e0c1565f6d9b6908c7359018c56d1bdaa..35fffcc98ebb9374656b9f4fee90945e15c89d3b 100644 --- a/include/staff/login.tpl.php +++ b/include/staff/login.tpl.php @@ -12,7 +12,7 @@ $info = ($_POST && $errors)?Format::htmlchars($_POST):array(); <input type="text" name="userid" id="name" value="<?php echo $info['userid']; ?>" placeholder="username" autocorrect="off" autocapitalize="off"> <input type="password" name="passwd" id="pass" placeholder="password" autocorrect="off" autocapitalize="off"> </fieldset> - <?php if ($_SESSION['_staff']['strikes'] > 1 && $cfg->allowPasswordReset()) { ?> + <?php if ($show_reset && $cfg->allowPasswordReset()) { ?> <h3 style="display:inline"><a href="pwreset.php">Forgot my password</a></h3> <?php } ?> <input class="submit" type="submit" name="submit" value="Log In"> diff --git a/include/staff/plugin-add.inc.php b/include/staff/plugin-add.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..46251f66d1c2269e8c2417ed0e238613d1e1f8e6 --- /dev/null +++ b/include/staff/plugin-add.inc.php @@ -0,0 +1,35 @@ + +<h2>Install a new plugin</h2> +<p> +To add a plugin into the system, download and place the plugin into the +<code>include/plugins</code> folder. Once in the plugin is in the +<code>plugins/</code> folder, it will be shown in the list below. +</p> + +<form method="post" action="?"> + <?php echo csrf_token(); ?> + <input type="hidden" name="do" value="install"/> +<table class="list" width="100%"><tbody> +<?php + +$installed = $ost->plugins->allInstalled(); +foreach ($ost->plugins->allInfos() as $info) { + // Ignore installed plugins + if (isset($installed[$info['install_path']])) + continue; + ?> + <tr><td><button type="submit" name="install_path" + value="<?php echo $info['install_path']; + ?>">Install</button></td> + <td> + <div><strong><?php echo $info['name']; ?></strong><br/> + <div><?php echo $info['description']; ?></div> + <div class="faded"><em>Version: <?php echo $info['version']; ?></em></div> + <div class="faded"><em>Author: <?php echo $info['author']; ?></em></div> + </div> + </td></tr> + <?php +} +?> +</tbody></table> +</form> diff --git a/include/staff/plugin.inc.php b/include/staff/plugin.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..c1cefd5bc2fb60e1570b97e7fec9e6b838461bf9 --- /dev/null +++ b/include/staff/plugin.inc.php @@ -0,0 +1,44 @@ +<?php + +$info=array(); +if($plugin && $_REQUEST['a']!='add') { + $config = $plugin->getConfig(); + if ($config) + $form = $config->getForm(); + if ($_POST) + $form->isValid(); + $title = 'Update Plugin'; + $action = 'update'; + $submit_text='Save Changes'; + $info = $plugin->ht; +} +$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); +?> + +<form action="?id=<?php echo urlencode($_REQUEST['id']); ?>" method="post" id="save"> + <?php csrf_token(); ?> + <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> + <h2>Manage Plugin + <br/><small><?php echo $plugin->getName(); ?></small></h2> + + <h3>Configuration</h3> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <tbody> +<?php +if ($form) + $form->render(); +else { ?> + <tr><th>This plugin has no configurable settings<br> + <em>Every plugin should be so easy to use</em></th></tr> +<?php } +?> + </tbody></table> +<p class="centered"> +<?php if ($form) { ?> + <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> + <input type="reset" name="reset" value="Reset"> +<?php } ?> + <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'> +</p> +</form> diff --git a/include/staff/plugins.inc.php b/include/staff/plugins.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..a25ccc095a1c11a1544d2c24d3666d185ee0ca70 --- /dev/null +++ b/include/staff/plugins.inc.php @@ -0,0 +1,96 @@ +<div style="width:700;padding-top:5px; float:left;"> + <h2>Currently Installed Plugins</h2> +</div> +<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> + <b><a href="plugins.php?a=add" class="Icon form-add">Add New Plugin</a></b></div> +<div class="clear"></div> + +<?php +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = count($ost->plugins->allInstalled()); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$pageNav->setURL('forms.php'); +$showing=$pageNav->showing().' forms'; +?> + +<form action="plugins.php" method="POST" name="forms"> +<?php csrf_token(); ?> +<input type="hidden" name="do" value="mass_process" > +<input type="hidden" id="action" name="a" value="" > +<table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> + <thead> + <tr> + <th width="7"> </th> + <th>Plugin Name</th> + <th>Status</td> + <th>Date Installed</th> + </tr> + </thead> + <tbody> +<?php +foreach ($ost->plugins->allInstalled() as $p) { + if ($p instanceof Plugin) { ?> + <tr> + <td><input type="checkbox" class="ckb" name="ids[]" value="<?php echo $p->getId(); ?>" + <?php echo $sel?'checked="checked"':''; ?>></td> + <td><a href="plugins.php?id=<?php echo $p->getId(); ?>" + ><?php echo $p->getName(); ?></a></td> + <td><?php echo ($p->isActive()) + ? 'Enabled' : '<strong>Disabled</strong>'; ?></td> + <td><?php echo Format::db_datetime($p->getInstallDate()); ?></td> + </tr> + <?php } else {} ?> +<?php } ?> + </tbody> + <tfoot> + <tr> + <td colspan="4"> + <?php if($count){ ?> + Select: + <a id="selectAll" href="#ckb">All</a> + <a id="selectNone" href="#ckb">None</a> + <a id="selectToggle" href="#ckb">Toggle</a> + <?php }else{ + echo 'No plugins installed yet — <a href="?a=add">add one</a>!'; + } ?> + </td> + </tr> + </tfoot> +</table> +<?php +if ($count) //Show options.. + echo '<div> Page:'.$pageNav->getPageLinks().' </div>'; +?> +<p class="centered" id="actions"> + <input class="button" type="submit" name="delete" value="Delete"> + <input class="button" type="submit" name="enable" value="Enable"> + <input class="button" type="submit" name="disable" value="Disable"> +</p> +</form> + +<div style="display:none;" class="dialog" id="confirm-action"> + <h3>Please Confirm</h3> + <a class="close" href="">×</a> + <hr/> + <p class="confirm-action" style="display:none;" id="delete-confirm"> + <font color="red"><strong>Are you sure you want to DELETE selected plugins?</strong></font> + <br><br>Deleted forms CANNOT be recovered. + </p> + <p class="confirm-action" style="display:none;" id="enable-confirm"> + <font color="green"><strong>Are you ready to enable selected plugins?</strong></font> + </p> + <p class="confirm-action" style="display:none;" id="disable-confirm"> + <font color="red"><strong>Are you sure you want to disable selected plugins?</strong></font> + </p> + <div>Please confirm to continue.</div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="button" value="No, Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="button" value="Yes, Do it!" class="confirm"> + </span> + </p> + <div class="clear"></div> +</div> diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php index 8ceaca328452056ad3829a00ae69f64c85dae52e..116a39eab0617425965850687298f47999bfb77d 100644 --- a/include/staff/profile.inc.php +++ b/include/staff/profile.inc.php @@ -100,6 +100,24 @@ $info['id']=$staff->getId(); <span class="error">* <?php echo $errors['timezone_id']; ?></span> </td> </tr> + <tr> + <td width="180"> + Preferred Language: + </td> + <td> + <?php + $langs = Internationalization::availableLanguages(); ?> + <select name="lang"> + <option value="">— Use Browser Preference —</option> +<?php foreach($langs as $l) { + $selected = ($info['lang'] == $l['code']) ? 'selected="selected"' : ''; ?> + <option value="<?php echo $l['code']; ?>" <?php echo $selected; + ?>><?php echo $l['desc']; ?></option> +<?php } ?> + </select> + <span class="error"> <?php echo $errors['lang']; ?></span> + </td> + </tr> <tr> <td width="180"> Daylight Saving: diff --git a/include/staff/settings-autoresp.inc.php b/include/staff/settings-autoresp.inc.php index 9123164c6a972a28a0e1a8a07a3d3de9a6b7f90e..927bc56a5b3c94ffb0ea14835b6682c99574014f 100644 --- a/include/staff/settings-autoresp.inc.php +++ b/include/staff/settings-autoresp.inc.php @@ -16,36 +16,45 @@ <tr> <td width="160">New Ticket:</td> <td> - <input type="radio" name="ticket_autoresponder" value="1" <?php echo $config['ticket_autoresponder']?'checked="checked"':''; ?> /><b>Enable</b> - <input type="radio" name="ticket_autoresponder" value="0" <?php echo !$config['ticket_autoresponder']?'checked="checked"':''; ?> />Disable - + <input type="checkbox" name="ticket_autoresponder" <?php +echo $config['ticket_autoresponder'] ? 'checked="checked"' : ''; ?>/> + Ticket Owner <i class="help-tip icon-question-sign" href="#new_ticket"></i> </td> </tr> <tr> - <td width="160">New Ticket by staff:</td> + <td width="160">New Ticket by Staff:</td> <td> - <input type="radio" name="ticket_notice_active" value="1" <?php echo $config['ticket_notice_active']?'checked="checked"':''; ?> /><b>Enable</b> - <input type="radio" name="ticket_notice_active" value="0" <?php echo !$config['ticket_notice_active']?'checked="checked"':''; ?> />Disable - + <input type="checkbox" name="ticket_notice_active" <?php +echo $config['ticket_notice_active'] ? 'checked="checked"' : ''; ?>/> + Ticket Owner <i class="help-tip icon-question-sign" href="#new_staff_ticket"></i> </td> </tr> <tr> - <td width="160">New Message:</td> + <td width="160" rowspan="2">New Message:</td> <td> - <input type="radio" name="message_autoresponder" value="1" <?php echo $config['message_autoresponder']?'checked="checked"':''; ?> /><b>Enable</b> - <input type="radio" name="message_autoresponder" value="0" <?php echo !$config['message_autoresponder']?'checked="checked"':''; ?> />Disable - + <input type="checkbox" name="message_autoresponder" <?php +echo $config['message_autoresponder'] ? 'checked="checked"' : ''; ?>/> + Submitter: Send receipt confirmation <i class="help-tip icon-question-sign" href="#new_message"></i> </td> </tr> <tr> - <td width="160">Overlimit notice:</td> <td> - <input type="radio" name="overlimit_notice_active" value="1" <?php echo $config['overlimit_notice_active']?'checked="checked"':''; ?> /><b>Enable</b> - <input type="radio" name="overlimit_notice_active" value="0" <?php echo !$config['overlimit_notice_active']?'checked="checked"':''; ?> />Disable - + <input type="checkbox" name="message_autoresponder_collabs" <?php +echo $config['message_autoresponder_collabs'] ? 'checked="checked"' : ''; ?>/> + Participants: Send new activity notice + <i class="help-tip icon-question-sign" href="#collaborators"></i> + </div> + </td> + </tr> + <tr> + <td width="160">Overlimit Notice:</td> + <td> + <input type="checkbox" name="overlimit_notice_active" <?php +echo $config['overlimit_notice_active'] ? 'checked="checked"' : ''; ?>/> + Ticket Submitter <i class="help-tip icon-question-sign" href="#overlimit_notice"></i> </td> </tr> diff --git a/include/staff/settings-emails.inc.php b/include/staff/settings-emails.inc.php index 0ccbb959046a34b867ef68c324dc8adeffa3b18d..b4c95d0247d77b0199e60ca796d21aa433a26835 100644 --- a/include/staff/settings-emails.inc.php +++ b/include/staff/settings-emails.inc.php @@ -109,6 +109,21 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) <font class="error"> <?php echo $errors['reply_separator']; ?></font> </td> </tr> + <tr> + <td width="180">Emailed Tickets Priority:</td> + <td> + <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> > + <em>(Use email priority when available)</em> + <i class="help-tip icon-question-sign" href="#use_email_priority"></i> + </td> + </tr> + <tr> + <td width="180">Accept Email Collaborators:</td> + <td><input type="checkbox" name="add_email_collabs" <?php + echo $config['add_email_collabs'] ? 'checked="checked"' : ''; ?>/> + Automatically add collaborators from email fields + <i class="help-tip icon-question-sign" href="#add_email_collabs"></i> + </tr> <tr><th colspan=2><em><strong>Outgoing Emails</strong>: Default email only applies to outgoing emails without SMTP setting.</em></th></tr> <tr><td width="180">Default Outgoing Email:</td> <td> diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index ce7489d19ddd15863d7849d3c33930ee93029712..c3d204328c300e9f8d3235883cf52b1227a14499 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -76,13 +76,6 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <em>(Minutes to lock a ticket on activity - enter 0 to disable locking)</em> </td> </tr> - <tr> - <td width="180">Emailed Tickets Priority:</td> - <td> - <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> > - <em>(Use email priority when available)</em> - </td> - </tr> <tr> <td width="180">Show Related Tickets:</td> <td> @@ -135,6 +128,14 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) Enable rich text in ticket thread and autoresponse emails </td> </tr> + <tr> + <td>Allow Client Updates:</td> + <td> + <input type="checkbox" name="allow_client_updates" <?php + echo $config['allow_client_updates']?'checked="checked"':''; ?>> + Allow clients to update ticket details via the web portal + </td> + </tr> <tr> <th colspan="2"> <em><b>Attachments</b>: Size and max. uploads setting mainly apply to web tickets.</em> @@ -240,6 +241,20 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked="checked"':''; ?> >Email attachments to the user </td> </tr> + <?php if (($bks = FileStorageBackend::allRegistered()) + && count($bks) > 1) { ?> + <tr> + <td width="180">Store Attachments:</td> + <td><select name="default_storage_bk"><?php + foreach ($bks as $char=>$class) { + $selected = $config['default_storage_bk'] == $char + ? 'selected="selected"' : ''; + ?><option <?php echo $selected; ?> value="<?php echo $char; ?>" + ><?php echo $class::$desc; ?></option><?php + } ?> + </td> + </tr> + <?php } ?> <tr> <th colspan="2"> <em><strong>Accepted File Types</strong>: Limit the type of files users are allowed to submit. diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php index 4567624842d7aaee268dfe7e034e4173b82c2afd..55351fbee7864581e1294a72e56d758d2d068c48 100644 --- a/include/staff/staff.inc.php +++ b/include/staff/staff.inc.php @@ -17,12 +17,12 @@ if($staff && $_REQUEST['a']!='add'){ $title='Add New Staff'; $action='create'; $submit_text='Add Staff'; - $passwd_text='Temp. password required <span class="error"> *</span>'; + $passwd_text='Temporary password required only for "Local" authenication'; //Some defaults for new staff. $info['change_passwd']=1; $info['isactive']=1; $info['isvisible']=1; - $info['isadmin']=0; + $info['isadmin']=0; $qstr.='&a=add'; } $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); @@ -48,7 +48,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Username: </td> <td> - <input type="text" size="30" name="username" value="<?php echo $info['username']; ?>"> + <input type="text" size="30" class="staff-username typeahead" + name="username" value="<?php echo $info['username']; ?>"> <span class="error">* <?php echo $errors['username']; ?></span> </td> </tr> @@ -58,7 +59,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); First Name: </td> <td> - <input type="text" size="30" name="firstname" value="<?php echo $info['firstname']; ?>"> + <input type="text" size="30" name="firstname" class="auto first" + value="<?php echo $info['firstname']; ?>"> <span class="error">* <?php echo $errors['firstname']; ?></span> </td> </tr> @@ -67,7 +69,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Last Name: </td> <td> - <input type="text" size="30" name="lastname" value="<?php echo $info['lastname']; ?>"> + <input type="text" size="30" name="lastname" class="auto last" + value="<?php echo $info['lastname']; ?>"> <span class="error">* <?php echo $errors['lastname']; ?></span> </td> </tr> @@ -76,7 +79,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Email Address: </td> <td> - <input type="text" size="30" name="email" value="<?php echo $info['email']; ?>"> + <input type="text" size="30" name="email" class="auto email" + value="<?php echo $info['email']; ?>"> <span class="error">* <?php echo $errors['email']; ?></span> </td> </tr> @@ -85,7 +89,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Phone Number: </td> <td> - <input type="text" size="18" name="phone" value="<?php echo $info['phone']; ?>"> + <input type="text" size="18" name="phone" class="auto phone" + value="<?php echo $info['phone']; ?>"> <span class="error"> <?php echo $errors['phone']; ?></span> Ext <input type="text" size="5" name="phone_ext" value="<?php echo $info['phone_ext']; ?>"> <span class="error"> <?php echo $errors['phone_ext']; ?></span> @@ -96,15 +101,39 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Mobile Number: </td> <td> - <input type="text" size="18" name="mobile" value="<?php echo $info['mobile']; ?>"> + <input type="text" size="18" name="mobile" class="auto mobile" + value="<?php echo $info['mobile']; ?>"> <span class="error"> <?php echo $errors['mobile']; ?></span> </td> </tr> <tr> <th colspan="2"> - <em><strong>Account Password</strong>: <?php echo $passwd_text; ?> <span class="error"> <?php echo $errors['temppasswd']; ?></span></em> + <em><strong>Authentication</strong>: <?php echo $passwd_text; ?> <span class="error"> <?php echo $errors['temppasswd']; ?></span></em> </th> </tr> + <tr> + <td>Authentication Backend</td> + <td> + <select name="backend" onchange="javascript: + if (this.value != '' && this.value != 'local') + $('#password-fields').hide(); + else + $('#password-fields').show(); + "> + <option value="">— Use any available backend —</option> + <?php foreach (StaffAuthenticationBackend::allRegistered() as $ab) { + if (!$ab->supportsAuthentication()) continue; ?> + <option value="<?php echo $ab::$id; ?>" <?php + if ($info['backend'] == $ab::$id) + echo 'selected="selected"'; ?>><?php + echo $ab::$name; ?></option> + <?php } ?> + </select> + </td> + </tr> + </tbody> + <tbody id="password-fields" style="<?php if ($info['backend'] && $info['backend'] != 'local') + echo 'display:none;'; ?>"> <tr> <td width="180"> Password: @@ -133,6 +162,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <strong>Force</strong> password change on next login. </td> </tr> + </tbody> + <tbody> <tr> <th colspan="2"> <em><strong>Staff's Signature</strong>: Optional signature used on outgoing emails. <span class="error"> <?php echo $errors['signature']; ?></span></em> diff --git a/include/staff/template.inc.php b/include/staff/template.inc.php index 6192eb67d0eacad2a54448c5a287a966daa7c26e..84e7704cd6799a4c598e57243e1bfecbdbe8563d 100644 --- a/include/staff/template.inc.php +++ b/include/staff/template.inc.php @@ -15,6 +15,7 @@ if($template && $_REQUEST['a']!='add'){ $action='add'; $submit_text='Add Template'; $info['isactive']=isset($info['isactive'])?$info['isactive']:0; + $info['lang_id'] = $cfg->getSystemLanguage(); $qstr.='&a='.urlencode($_REQUEST['a']); } $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); @@ -54,19 +55,23 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <span class="error">* <?php echo $errors['isactive']; ?></span> </td> </tr> + <?php + if($template){ ?> <tr> <td width="180" class="required"> Language: </td> <td> - <select name="lang_id"> - <option value="en" selected="selected">English (US)</option> - </select> - <span class="error">* <?php echo $errors['lang_id']; ?></span> + <?php + $langs = Internationalization::availableLanguages(); + $lang = strtolower($info['lang']); + if (isset($langs[$lang])) + echo $langs[$lang]['desc']; + else + echo $info['lang']; ?> </td> </tr> <?php - if($template){ $current_group = false; $impl = $template->getTemplates(); $_tpls = $template::$all_names; @@ -101,6 +106,23 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); } } # endfor } else { ?> + <tr> + <td width="180" class="required"> + Language: + </td> + <td> + <?php + $langs = Internationalization::availableLanguages(); ?> + <select name="lang_id"> +<?php foreach($langs as $l) { + $selected = ($info['lang_id'] == $l['code']) ? 'selected="selected"' : ''; ?> + <option value="<?php echo $l['code']; ?>" <?php echo $selected; + ?>><?php echo $l['desc']; ?></option> +<?php } ?> + </select> + <span class="error">* <?php echo $errors['lang_id']; ?></span> + </td> + </tr> <tr> <td width="180" class="required"> Template To Clone: diff --git a/include/staff/templates/collaborators-preview.tmpl.php b/include/staff/templates/collaborators-preview.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..bd9a3b6d0d25199c94664b86566da1ca8eec6a1d --- /dev/null +++ b/include/staff/templates/collaborators-preview.tmpl.php @@ -0,0 +1,44 @@ +<div> +<table border="0" cellspacing="" cellpadding="1"> +<colgroup><col style="min-width: 250px;"></col></colgroup> +<?php +if (($users=$ticket->getCollaborators())) {?> +<?php + foreach($users as $user) { + echo sprintf('<tr><td %s><i class="icon-%s"></i> %s <em><%s></em></td></tr>', + ($user->isActive()? '' : 'class="faded"'), + ($user->isActive()? 'comments' : 'comment-alt'), + $user->getName(), + $user->getEmail()); + } +} else { + echo "<strong>Ticket doesn't have collaborators.</strong>"; +}?> +</table> +<?php +$options = array(); + +$options[] = sprintf( + '<a class="collaborators" id="managecollab" href="#tickets/%d/collaborators">%s</a>', + $ticket->getId(), + $ticket->getNumCollaborators() + ? 'Manage Collaborators' : 'Add Collaborator' + ); + +if ($options) { + echo '<ul class="tip_menu">'; + foreach($options as $option) + echo sprintf('<li>%s</li>', $option); + echo '</ul>'; +} +?> +</div> +<script type="text/javascript"> +$(function() { + $(document).on('click', 'a#managecollab', function (e) { + e.preventDefault(); + $('.tip_box').remove(); + return false; + }); +}); +</script> diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..2c0de2a2ed82404b4ea3f0dbf39d9f7a79321df3 --- /dev/null +++ b/include/staff/templates/collaborators.tmpl.php @@ -0,0 +1,125 @@ +<h3>Ticket Collaborators</h3> +<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> +<?php +if($info && $info['msg']) { + echo sprintf('<p id="msg_notice" style="padding-top:2px;">%s</p>', $info['msg']); +} ?> +<hr/> +<?php +if(($users=$ticket->getCollaborators())) {?> +<div id="manage_collaborators"> +<form method="post" class="collaborators" action="#tickets/<?php echo $ticket->getId(); ?>/collaborators"> + <table border="0" cellspacing="1" cellpadding="1" width="100%"> + <?php + foreach($users as $user) { + $checked = $user->isActive() ? 'checked="checked"' : ''; + echo sprintf('<tr> + <td> + <input type="checkbox" name="cid[]" id="c%d" value="%d" %s> + <a class="collaborator" href="#collaborators/%d/view">%s</a> + <span class="faded"><em>%s</em></span></td> + <td width="10"> + <input type="hidden" name="del[]" id="d%d" value=""> + <a class="remove" href="#d%d">×</a></td> + <td width="30"> </td> + </tr>', + $user->getId(), + $user->getId(), + $checked, + $user->getId(), + $user->getName(), + $user->getEmail(), + $user->getId(), + $user->getId()); + } + ?> + </table> + <hr style="margin-top:1em"/> + <div><a class="collaborator" + href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator" >Add New Collaborator</a></div> + <div id="savewarning" style="display:none; padding-top:2px;"><p id="msg_warning">You have made changes that you need to save.</p></div> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="button" value="Cancel" class="close"> + <input type="reset" value="Reset"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="Save Changes"> + </span> + </p> +</form> +<div class="clear"></div> +</div> +<?php +} else { + echo "Bro, not sure how you got here!"; +} + +if ($_POST && $ticket && $ticket->getNumCollaborators()) { + $recipients = sprintf('Recipients (%d of %d)', + $ticket->getNumActiveCollaborators(), + $ticket->getNumCollaborators()); + ?> + <script type="text/javascript"> + $(function() { + $('#emailcollab').show(); + $('#recipients').html('<?php echo $recipients; ?>'); + }); + </script> +<?php +} +?> + +<script type="text/javascript"> +$(function() { + + $(document).on('click', 'form.collaborators a#addcollaborator', function (e) { + e.preventDefault(); + $('div#manage_collaborators').hide(); + $('div#add_collaborator').fadeIn(); + return false; + }); + + $(document).on('click', 'form.collaborators a.remove', function (e) { + e.preventDefault(); + var fObj = $(this).closest('form'); + $('input'+$(this).attr('href')) + .val($(this).attr('href').substr(2)) + .trigger('change'); + $(this).closest('tr').addClass('strike'); + + return false; + }); + + $(document).on('change', 'form.collaborators input:checkbox, input[name="del[]"]', function (e) { + var fObj = $(this).closest('form'); + $('div#savewarning', fObj).fadeIn(); + $('input:submit', fObj).css('color', 'red'); + }); + + $(document).on('click', 'form.collaborators input:reset', function(e) { + var fObj = $(this).closest('form'); + fObj.find('input[name="del[]"]').val(''); + fObj.find('tr').removeClass('strike'); + $('div#savewarning', fObj).hide(); + $('input:submit', fObj).removeAttr('style'); + }); + + $(document).on('click', 'form.collaborators input.cancel', function (e) { + e.preventDefault(); + var $elem = $(this); + + if($elem.attr('data-href')) { + var href = $elem.data('href').substr(1); + $('.dialog.collaborators .body').load('ajax.php/'+href, function () { + }); + } else { + + $('div#manage_collaborators').show(); + $('div#add_collaborator').hide(); + } + return false; + }); + +}); +</script> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index a05b0c70bce8bfba5ede01e00718f1cf3f580ad8..09d97ab650ab3d79df393153cda76fb999eeb980 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -13,7 +13,7 @@ <?php } else { ?> - <td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>"> + <td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>" style="min-width:120px;"> <?php echo Format::htmlchars($field->get('label')); ?>:</td> <td><?php } @@ -29,7 +29,7 @@ } foreach ($field->errors() as $e) { ?> <br /> - <font class="error"><?php echo $e; ?></font> + <font class="error"><?php echo Format::htmlchars($e); ?></font> <?php } ?> </td> </tr> diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..82c3e392f80c91441330a5c9befee4e50e870752 --- /dev/null +++ b/include/staff/templates/ticket-preview.tmpl.php @@ -0,0 +1,172 @@ +<?php +/* + * Ticket Preview popup template + * + */ + +$staff=$ticket->getStaff(); +$lock=$ticket->getLock(); +$error=$msg=$warn=null; + +if($lock && $lock->getStaffId()==$thisstaff->getId()) + $warn.=' <span class="Icon lockedTicket">Ticket is locked by '.$lock->getStaffName().'</span>'; +elseif($ticket->isOverdue()) + $warn.=' <span class="Icon overdueTicket">Marked overdue!</span>'; + +echo sprintf( + '<div style="width:600px; padding: 2px 2px 0 5px;" id="t%s"> + <h2>Ticket #%s: %s</h2><br>', + $ticket->getNumber(), + $ticket->getNumber(), + Format::htmlchars($ticket->getSubject())); + +if($error) + echo sprintf('<div id="msg_error">%s</div>',$error); +elseif($msg) + echo sprintf('<div id="msg_notice">%s</div>',$msg); +elseif($warn) + echo sprintf('<div id="msg_warning">%s</div>',$warn); + +echo '<ul class="tabs">'; + +echo ' + <li><a id="preview_tab" href="#preview" class="active" + ><i class="icon-list-alt"></i> Ticket Summary</a></li>'; +if ($ticket->getNumCollaborators()) { +echo sprintf(' + <li><a id="collab_tab" href="#collab" + ><i class="icon-fixed-width icon-group + faded"></i> Collaborators (%d)</a></li>', + $ticket->getNumCollaborators()); +} +echo '</ul>'; + +echo '<div class="tab_content" id="preview">'; +echo '<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; + +$ticket_state=sprintf('<span>%s</span>',ucfirst($ticket->getStatus())); +if($ticket->isOpen()) { + if($ticket->isOverdue()) + $ticket_state.=' — <span>Overdue</span>'; + else + $ticket_state.=sprintf(' — <span>%s</span>',$ticket->getPriority()); +} + +echo sprintf(' + <tr> + <th width="100">Ticket State:</th> + <td>%s</td> + </tr> + <tr> + <th>Create Date:</th> + <td>%s</td> + </tr>',$ticket_state, + Format::db_datetime($ticket->getCreateDate())); +if($ticket->isClosed()) { + echo sprintf(' + <tr> + <th>Close Date:</th> + <td>%s <span class="faded">by %s</span></td> + </tr>', + Format::db_datetime($ticket->getCloseDate()), + ($staff?$staff->getName():'staff') + ); +} elseif($ticket->getEstDueDate()) { + echo sprintf(' + <tr> + <th>Due Date:</th> + <td>%s</td> + </tr>', + Format::db_datetime($ticket->getEstDueDate())); +} +echo '</table>'; + + +echo '<hr> + <table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">'; +if($ticket->isOpen()) { + echo sprintf(' + <tr> + <th width="100">Assigned To:</th> + <td>%s</td> + </tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">— Unassigned —</span>'); +} +echo sprintf( + ' + <tr> + <th>From:</th> + <td>%s <span class="faded">%s</span></td> + </tr> + <tr> + <th width="100">Department:</th> + <td>%s</td> + </tr> + <tr> + <th>Help Topic:</th> + <td>%s</td> + </tr>', + Format::htmlchars($ticket->getName()), + $ticket->getEmail(), + Format::htmlchars($ticket->getDeptName()), + Format::htmlchars($ticket->getHelpTopic())); + +echo ' + </table>'; +echo '</div>'; // ticket preview content. +?> +<div class="tab_content" id="collab" style="display:none;"> + <table border="0" cellspacing="" cellpadding="1"> + <colgroup><col style="min-width: 250px;"></col></colgroup> + <?php + if (($users=$ticket->getCollaborators())) {?> + <?php + foreach($users as $user) { + echo sprintf('<tr><td %s><i class="icon-%s"></i> %s <em><%s></em></td></tr>', + ($user->isActive()? '' : 'class="faded"'), + ($user->isActive()? 'comments' : 'comment-alt'), + $user->getName(), + $user->getEmail()); + } + } else { + echo "Ticket doesn't have collaborators."; + }?> + </table> + <br> + <?php + echo sprintf('<span><a class="collaborators" + href="#tickets/%d/collaborators">%s</a></span>', + $ticket->getId(), + $ticket->getNumCollaborators() + ? 'Manage Collaborators' : 'Add Collaborator' + ); + ?> +</div> +<?php +$options = array(); +$options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid"); +if($ticket->getNumNotes()) + $options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes"); + +if($ticket->isOpen()) + $options[]=array('action'=>'Reply','url'=>"tickets.php?id=$tid#reply"); + +if($thisstaff->canAssignTickets()) + $options[]=array('action'=>($ticket->isAssigned()?'Reassign':'Assign'),'url'=>"tickets.php?id=$tid#assign"); + +if($thisstaff->canTransferTickets()) + $options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer"); + +$options[]=array('action'=>'Post Note','url'=>"tickets.php?id=$tid#note"); + +if($thisstaff->canEditTickets()) + $options[]=array('action'=>'Edit Ticket','url'=>"tickets.php?id=$tid&a=edit"); + +if($options) { + echo '<ul class="tip_menu">'; + foreach($options as $option) + echo sprintf('<li><a href="%s">%s</a></li>',$option['url'],$option['action']); + echo '</ul>'; +} + +echo '</div>'; +?> diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php index 2eda5c1431f027dedde61c83dc1f12a78d95c813..3604ed5a29ddff2246343d69a41a32a9cad810fc 100644 --- a/include/staff/templates/user-lookup.tmpl.php +++ b/include/staff/templates/user-lookup.tmpl.php @@ -11,7 +11,7 @@ if ($info['error']) { echo sprintf('<p id="msg_notice">%s</p>', $info['msg']); } ?> <div id="selected-user-info" style="display:<?php echo $user ? 'block' :'none'; ?>;margin:5px;"> -<form method="get" class="user" action="#users/lookup"> +<form method="post" class="user" action="<?php echo $info['action'] ? $info['action'] : '#users/lookup'; ?>"> <input type="hidden" id="user-id" name="id" value="<?php echo $user ? $user->getId() : 0; ?>"/> <i class="icon-user icon-4x pull-left icon-border"></i> <a class="action-button pull-right" style="overflow:inherit" @@ -46,7 +46,7 @@ if ($info['error']) { </form> </div> <div id="new-user-form" style="display:<?php echo $user ? 'none' :'block'; ?>;"> -<form method="post" class="user" action="#users/lookup/form"> +<form method="post" class="user" action="<?php echo $info['action'] ?: '#users/lookup/form'; ?>"> <table width="100%" class="fixed"> <?php if(!$form) $form = UserForm::getInstance(); @@ -68,9 +68,11 @@ if ($info['error']) { </div> <script type="text/javascript"> $(function() { + var last_req; $('#user-search').typeahead({ source: function (typeahead, query) { - $.ajax({ + if (last_req) last_req.abort(); + last_req = $.ajax({ url: "ajax.php/users?q="+query, dataType: 'json', success: function (data) { @@ -80,7 +82,7 @@ $(function() { }, onselect: function (obj) { $('#the-lookup-form').load( - "ajax.php/users/select/"+obj.id + '<?php echo $info['onselect']? $info['onselect']: "ajax.php/users/select/"; ?>'+encodeURIComponent(obj.id) ); }, property: "/bin/true" @@ -89,14 +91,14 @@ $(function() { $('a#unselect-user').click( function(e) { e.preventDefault(); $('div#selected-user-info').hide(); - $('div#new-user-form').fadeIn(); + $('div#new-user-form').fadeIn({start: function(){ $('#user-search').focus(); }}); return false; }); $(document).on('click', 'form.user input.cancel', function (e) { e.preventDefault(); $('div#new-user-form').hide(); - $('div#selected-user-info').fadeIn(); + $('div#selected-user-info').fadeIn({start: function(){ $('#user-search').focus(); }}); return false; }); }); diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php index e2f2fd3cd9b7a4f4eb78be207a549026d6a6790b..6c91f3b26416d0b0684a2fc9b1d7efa0ca4cc001 100644 --- a/include/staff/templates/user.tmpl.php +++ b/include/staff/templates/user.tmpl.php @@ -43,7 +43,7 @@ if ($info['error']) { <div id="user-form" style="display:<?php echo $forms ? 'block' : 'none'; ?>;"> <div><p id="msg_info"><i class="icon-info-sign"></i> Please note that updates will be reflected system-wide.</p></div> <?php -$action = '#users/'.$user->getId(); +$action = $info['action'] ? $info['action'] : ('#users/'.$user->getId()); if ($ticket && $ticket->getOwnerId() == $user->getId()) $action = '#tickets/'.$ticket->getId().'/user'; ?> diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index cddb2ee3c6b36f248110fb85833cdb87760b9b9a..e60f275c2633c237a51f91eeab1b048bf0abf980 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -11,7 +11,7 @@ if ($_POST) <input type="hidden" name="do" value="update"> <input type="hidden" name="a" value="edit"> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> - <h2>Update Ticket #<?php echo $ticket->getExtId(); ?></h2> + <h2>Update Ticket #<?php echo $ticket->getNumber(); ?></h2> <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> <tbody> <tr> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index fa4b0ee15569798ac5831a70c04a4fa7374cae36..b472f46764306fbe7cac85779f62c485b858e75e 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -270,9 +270,15 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <label><input type='checkbox' value='1' name="append" id="append" checked="checked">Append</label> </div> <?php - } ?> + } + $signature = ''; + if ($thisstaff->getDefaultSignatureType() == 'mine') + $signature = $thisstaff->getSignature(); ?> <textarea class="richtext ifhtml draft draft-delete" data-draft-namespace="ticket.staff.response" + data-signature="<?php + echo Format::htmlchars(Format::viewableImages($signature)); ?>" + data-signature-field="signature" data-dept-field="deptId" placeholder="Intial response for the ticket" name="response" id="response" cols="21" rows="8" style="width:80%;"><?php echo $info['response']; ?></textarea> @@ -286,7 +292,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); if($info['cannedattachments']) { foreach($info['cannedattachments'] as $k=>$id) { if(!($file=AttachmentFile::lookup($id))) continue; - $hash=$file->getHash().md5($file->getId().session_id().$file->getHash()); + $hash=$file->getKey().md5($file->getId().session_id().$file->getKey()); echo sprintf('<label><input type="checkbox" name="cannedattachments[]" id="f%d" value="%d" checked="checked" <a href="file.php?h=%s">%s</a> </label> ', diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 0cf0e0dff7965388301c67ab6b88ba10545cebdf..9568dbf656c5513a28980f101ee35089368643f3 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -15,6 +15,7 @@ if($cfg->getLockTime() && !$ticket->acquireLock($thisstaff->getId(),$cfg->getLoc //Get the goodies. $dept = $ticket->getDept(); //Dept $staff = $ticket->getStaff(); //Assigned or closed by.. +$user = $ticket->getOwner(); //Ticket User (EndUser) $team = $ticket->getTeam(); //Assigned team. $sla = $ticket->getSLA(); $lock = $ticket->getLock(); //Ticket lock obj @@ -40,7 +41,8 @@ if($ticket->isOverdue()) <table width="940" cellpadding="2" cellspacing="0" border="0"> <tr> <td width="50%" class="has_bottom_border"> - <h2><a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="Reload"><i class="icon-refresh"></i> Ticket #<?php echo $ticket->getExtId(); ?></a></h2> + <h2><a href="tickets.php?id=<?php echo $ticket->getId(); ?>" + title="Reload"><i class="icon-refresh"></i> Ticket #<?php echo $ticket->getNumber(); ?></a></h2> </td> <td width="50%" class="right_align has_bottom_border"> <?php @@ -154,29 +156,28 @@ if($ticket->isOverdue()) $('#user-'+user.id+'-name').text(user.name); $('#user-'+user.id+'-email').text(user.email); $('#user-'+user.id+'-phone').text(user.phone); - $('#user-to-name').text(user.name); - $('#user-to-email').text(user.email); + $('select#emailreply option[value=1]').text(user.name+' <'+user.email+'>'); }); return false; "><i class="icon-user"></i> <span id="user-<?php echo $ticket->getOwnerId(); ?>-name" ><?php echo Format::htmlchars($ticket->getName()); ?></span></a> <?php - if(($client=$ticket->getClient())) { - echo sprintf(' <a href="tickets.php?a=search&ownerId=%d" title="Related Tickets" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>', - urlencode($ticket->getOwnerId()), $client->getNumTickets()); + if($user) { + echo sprintf(' <a href="tickets.php?a=search&uid=%d" title="Related Tickets" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>', + urlencode($user->getId()), $user->getNumTickets()); ?> <div id="action-dropdown-stats" class="action-dropdown anchor-right"> <ul> <?php - if(($open=$client->getNumOpenTickets())) - echo sprintf('<li><a href="tickets.php?a=search&status=open&ownerId=%s"><i class="icon-folder-open-alt"></i> %d Open Tickets</a></li>', - $ticket->getOwnerId(), $open); - if(($closed=$client->getNumClosedTickets())) - echo sprintf('<li><a href="tickets.php?a=search&status=closed&ownerId=%d"><i class="icon-folder-close-alt"></i> %d Closed Tickets</a></li>', - $ticket->getOwnerId(), $closed); + if(($open=$user->getNumOpenTickets())) + echo sprintf('<li><a href="tickets.php?a=search&status=open&uid=%s"><i class="icon-folder-open-alt"></i> %d Open Tickets</a></li>', + $user->getId(), $open); + if(($closed=$user->getNumClosedTickets())) + echo sprintf('<li><a href="tickets.php?a=search&status=closed&uid=%d"><i class="icon-folder-close-alt"></i> %d Closed Tickets</a></li>', + $user->getId(), $closed); ?> - <li><a href="tickets.php?a=search&ownerId=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right"></i> All Tickets</a></li> + <li><a href="tickets.php?a=search&uid=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right"></i> All Tickets</a></li> </u> </div> <?php @@ -350,7 +351,7 @@ $tcount+= $ticket->getNumNotes(); <span style="vertical-align:middle;" class="textra"></span> <span style="vertical-align:middle;" class="tmeta faded title"><?php - echo Format::htmlchars($entry['poster']); ?></span> + echo Format::htmlchars($entry['name'] ?: $entry['poster']); ?></span> </span> </div> </th> @@ -418,21 +419,57 @@ $tcount+= $ticket->getNumNotes(); <input type="hidden" name="a" value="reply"> <span class="error"></span> <table style="width:100%" border="0" cellspacing="0" cellpadding="3"> + <tbody id="to_sec"> <tr> <td width="120"> <label><strong>TO:</strong></label> </td> <td> <?php - echo sprintf('<span id="user-to-name">%s</span> <em><<span id="user-to-email">%s</span>></em>', - Format::htmlChars($ticket->getName()), - $ticket->getReplyToEmail()); + # XXX: Add user-to-name and user-to-email HTML ID#s + $to =sprintf('%s <%s>', $ticket->getName(), $ticket->getReplyToEmail()); + $emailReply = (!isset($info['emailreply']) || $info['emailreply']); ?> - - <label><input type='checkbox' value='1' name="emailreply" id="remailreply" - <?php echo ((!$info['emailreply'] && !$errors) || isset($info['emailreply']))?'checked="checked"':''; ?>> Email Reply</label> + <select id="emailreply" name="emailreply"> + <option value="1" <?php echo $emailReply ? 'selected="selected"' : ''; ?>><?php echo $to; ?></option> + <option value="0" <?php echo !$emailReply ? 'selected="selected"' : ''; ?> + >—Do Not Email Reply—</option> + </select> </td> </tr> + </tbody> + <?php + if(1) { //Make CC optional feature? NO, for now. + ?> + <tbody id="cc_sec" + style="display:<?php echo $emailReply? 'table-row-group':'none'; ?>;"> + <tr> + <td width="120"> + <label><strong>Collaborators:</strong></label> + </td> + <td> + <input type='checkbox' value='1' name="emailcollab" id="emailcollab" + <?php echo ((!$info['emailcollab'] && !$errors) || isset($info['emailcollab']))?'checked="checked"':''; ?> + style="display:<?php echo $ticket->getNumCollaborators() ? 'inline-block': 'none'; ?>;" + > + <?php + $recipients = 'Add Recipients'; + if ($ticket->getNumCollaborators()) + $recipients = sprintf('Recipients (%d of %d)', + $ticket->getNumActiveCollaborators(), + $ticket->getNumCollaborators()); + + echo sprintf('<span><a class="collaborators preview" + href="#tickets/%d/collaborators"><span id="recipients">%s</span></a></span>', + $ticket->getId(), + $recipients); + ?> + </td> + </tr> + </tbody> + <?php + } ?> + <tbody id="resp_sec"> <?php if($errors['response']) {?> <tr><td width="120"> </td><td class="error"><?php echo $errors['response']; ?> </td></tr> @@ -457,10 +494,23 @@ $tcount+= $ticket->getNumNotes(); <label><input type='checkbox' value='1' name="append" id="append" checked="checked"> Append</label> <br> <?php - }?> + } + $signature = ''; + switch ($thisstaff->getDefaultSignatureType()) { + case 'dept': + if ($dept && $dept->canAppendSignature()) + $signature = $dept->getSignature(); + break; + case 'mine': + $signature = $thisstaff->getSignature(); + break; + } ?> <input type="hidden" name="draft_id" value=""/> <textarea name="response" id="response" cols="50" data-draft-namespace="ticket.response" + data-signature-field="signature" data-dept-id="<?php echo $dept->getId(); ?>" + data-signature="<?php + echo Format::htmlchars(Format::viewableImages($signature)); ?>" placeholder="Start writing your response here. Use canned responses from the drop-down above" data-draft-object-id="<?php echo $ticket->getId(); ?>" rows="9" wrap="soft" @@ -532,7 +582,7 @@ $tcount+= $ticket->getNumNotes(); </tr> <?php } ?> - </div> + </tbody> </table> <p style="padding-left:165px;"> <input class="btn_sm" type="submit" value="Post Reply"> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 9d72b72c958a9f2bb494d355e843195365f918c8..18642111a1d015cae7d26301612acd8298087a01 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -78,9 +78,10 @@ if($status) { $qwhere.=' AND ticket.status='.db_input(strtolower($status)); } -if (isset($_REQUEST['ownerId'])) { - $qwhere .= ' AND ticket.user_id='.db_input($_REQUEST['ownerId']); - $qstr .= '&ownerId='.urlencode($_REQUEST['ownerId']); +if (isset($_REQUEST['uid']) && $_REQUEST['uid']) { + $qwhere .= ' AND (ticket.user_id='.db_input($_REQUEST['uid']) + .' OR collab.user_id='.db_input($_REQUEST['uid']).') '; + $qstr .= '&uid='.urlencode($_REQUEST['uid']); } //Queues: Overloaded sub-statuses - you've got to just have faith! @@ -117,7 +118,7 @@ if($search): $qstr.='&query='.urlencode($searchTerm); $queryterm=db_real_escape($searchTerm,false); //escape the term ONLY...no quotes. if (is_numeric($searchTerm)) { - $qwhere.=" AND ticket.ticketID LIKE '$queryterm%'"; + $qwhere.=" AND ticket.`number` LIKE '$queryterm%'"; } elseif (strpos($searchTerm,'@') && Validator::is_email($searchTerm)) { //pulling all tricks! # XXX: What about searching for email addresses in the body of @@ -145,7 +146,7 @@ if ($_REQUEST['advsid'] && isset($_SESSION['adv_'.$_REQUEST['advsid']])) { db_input($_SESSION['adv_'.$_REQUEST['advsid']])).')'; } -$sortOptions=array('date'=>'effective_date','ID'=>'ticket.ticketID', +$sortOptions=array('date'=>'effective_date','ID'=>'ticket.`number`', 'pri'=>'pri.priority_urgency','name'=>'user.name','subj'=>'cdata.subject', 'status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff', 'dept'=>'dept_name'); @@ -197,7 +198,7 @@ $$x=' class="'.strtolower($order).'" '; if($_GET['limit']) $qstr.='&limit='.urlencode($_GET['limit']); -$qselect ='SELECT ticket.ticket_id,lock_id,ticketID,ticket.dept_id,ticket.staff_id,ticket.team_id ' +$qselect ='SELECT ticket.ticket_id,lock_id,`number`,ticket.dept_id,ticket.staff_id,ticket.team_id ' .' ,user.name' .' ,email.address as email, dept_name ' .' ,ticket.status,ticket.source,isoverdue,isanswered,ticket.created '; @@ -207,9 +208,15 @@ $qfrom=' FROM '.TICKET_TABLE.' ticket '. ' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'. ' LEFT JOIN '.DEPT_TABLE.' dept ON ticket.dept_id=dept.dept_id '; +if ($_REQUEST['uid']) + $qfrom.=' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab + ON (ticket.ticket_id = collab.ticket_id )'; + + $sjoin=''; + if($search && $deep_search) { - $sjoin=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'; + $sjoin.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'; } //get ticket count based on the query so far.. @@ -262,11 +269,15 @@ while ($row = db_fetch_array($res)) { // Fetch attachment and thread entry counts if ($results) { - $counts_sql = 'SELECT ticket.ticket_id, count(attach.attach_id) as attachments, - count(DISTINCT thread.id) as thread_count + $counts_sql = 'SELECT ticket.ticket_id, + count(DISTINCT attach.attach_id) as attachments, + count(DISTINCT thread.id) as thread_count, + count(DISTINCT collab.id) as collaborators FROM '.TICKET_TABLE.' ticket LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) ' .' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON ( ticket.ticket_id=thread.ticket_id) ' + .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab + ON ( ticket.ticket_id=collab.ticket_id) ' .' WHERE ticket.ticket_id IN ('.implode(',', db_input(array_keys($results))).') GROUP BY ticket.ticket_id'; $ids_res = db_query($counts_sql); @@ -379,7 +390,7 @@ if ($results) { }else{ $lc=Format::truncate($row['dept_name'],40); } - $tid=$row['ticketID']; + $tid=$row['number']; $subject = Format::htmlchars(Format::truncate($row['subject'],40)); $threadcount=$row['thread_count']; if(!strcasecmp($row['status'],'open') && !$row['isanswered'] && !$row['lock_id']) { @@ -401,11 +412,17 @@ if ($results) { <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview" title="Preview Ticket" href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $tid; ?></a></td> <td align="center" nowrap><?php echo Format::db_datetime($row['effective_date']); ?></td> - <td><a <?php if($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?> + <td><a <?php if ($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?> href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $subject; ?></a> - - <?php echo ($threadcount>1)?" <small>($threadcount)</small> ":''?> - <?php echo $row['attachments']?"<span class='Icon file'> </span>":''; ?> + <?php + if ($threadcount>1) + echo "<small>($threadcount)</small> ".'<i + class="icon-fixed-width icon-comments-alt"></i> '; + if ($row['collaborators']) + echo '<i class="icon-fixed-width icon-group faded"></i> '; + if ($row['attachments']) + echo '<i class="icon-fixed-width icon-paperclip"></i> '; + ?> </td> <td nowrap> <?php echo Format::truncate($row['name'],22,strpos($row['name'],'@')); ?> </td> <?php diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index cc5bdea3c043bacc39e0d2213dfa2c8b13c5a8f8..3ac67bf82846c663c880c6fc441e5e108e310989 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -6de40a4d5bad7a2923e769a4db1ff3b9 +f5692e24c7afba7ab6168dde0b3bb3c8 diff --git a/include/upgrader/streams/core/15b30765-dd0022fb.task.php b/include/upgrader/streams/core/15b30765-dd0022fb.task.php index bb2df98bdf338eefc81cc0c29777ea5b95ade1ce..4d7eac01ff94a7622a16b118b5c567d41d3a9af0 100644 --- a/include/upgrader/streams/core/15b30765-dd0022fb.task.php +++ b/include/upgrader/streams/core/15b30765-dd0022fb.task.php @@ -108,7 +108,7 @@ class AttachmentMigrater extends MigrationTask { } # TODO: Add extension-based mime-type lookup - if (!($fileId = AttachmentFile::save($info))) { + if (!($fileId = $this->saveAttachment($info))) { return $this->skip($info['attachId'], sprintf('%s: Unable to migrate attachment', $info['path'])); } @@ -227,6 +227,47 @@ class AttachmentMigrater extends MigrationTask { function getErrors() { return $this->errorList; } + + // This is the AttachmentFile::save() method from osTicket 1.7.6. It's + // been ported here so that further changes to the %file table and the + // AttachmentFile::save() method do not affect upgrades from osTicket + // 1.6 to osTicket 1.8 and beyond. + function saveAttachment($file) { + + if(!$file['hash']) + $file['hash']=MD5(md5_file($file['path']).time()); + $file['data'] = file_get_contents($file['path']); + if(!$file['size']) + $file['size']=strlen($file['data']); + + $sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() ' + .',type='.db_input($file['type']) + .',size='.db_input($file['size']) + .',name='.db_input($file['name']) + .',hash='.db_input($file['hash']); + + if (!(db_query($sql) && ($id=db_insert_id()))) + return false; + + $f = new CompatAttachmentFile($id); + $bk = new AttachmentChunkedData($f); + if (!$bk->write($file['data'])) + return false; + + return $id; + } +} + +class CompatAttachmentFile { + var $id; + + function __construct($id) { + $this->id = $id; + } + + function getId() { + return $this->id; + } } return 'AttachmentMigrater'; diff --git a/include/upgrader/streams/core/1b0fce99-ed60ba20.patch.sql b/include/upgrader/streams/core/1b0fce99-ed60ba20.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..3e6bea555155b9187423994612e4aec39244487b --- /dev/null +++ b/include/upgrader/streams/core/1b0fce99-ed60ba20.patch.sql @@ -0,0 +1,27 @@ +/** + * @version v1.8 - Collaboration + * @signature ed60ba203a473f4f32ac49eb45db16c7 + * @title Add support for ticket collaborators + * + * Adds the database structure for collaboration table + * + */ + +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_collaborator`; +CREATE TABLE `%TABLE_PREFIX%ticket_collaborator` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1', + `ticket_id` int(11) unsigned NOT NULL DEFAULT '0', + `user_id` int(11) unsigned NOT NULL DEFAULT '0', + -- M => (message) clients, N => (note) 3rd-Party, R => (reply) external authority + `role` char(1) NOT NULL DEFAULT 'M', + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `collab` (`ticket_id`,`user_id`) +) DEFAULT CHARSET=utf8; + + +-- Finish +UPDATE `%TABLE_PREFIX%config` + SET `value` = 'ed60ba203a473f4f32ac49eb45db16c7' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/6de40a4d-1b0fce99.patch.sql b/include/upgrader/streams/core/6de40a4d-1b0fce99.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..48f9480d54544b4eb94935817109cd930cce487d --- /dev/null +++ b/include/upgrader/streams/core/6de40a4d-1b0fce99.patch.sql @@ -0,0 +1,27 @@ +/** + * @version v1.8.1 - Plugins + * @schema 1b0fce992f6c7ed37a9b2914f86775d4 + * @title Add plugin management system + * + * Add table for plugin manager + */ + +ALTER TABLE `%TABLE_PREFIX%staff` + ADD `backend` varchar(32) default NULL AFTER `passwd`; + +-- Plugins +DROP TABLE IF EXISTS `%TABLE_PREFIX%plugin`; +CREATE TABLE `%TABLE_PREFIX%plugin` ( + `id` int(11) unsigned not null auto_increment, + `name` varchar(30) not null, + `install_path` varchar(60) not null, + `isphar` tinyint(1) not null default 0, + `isactive` tinyint(1) not null default 0, + `installed` datetime not null, + primary key (`id`) +) DEFAULT CHARSET=utf8; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = '1b0fce992f6c7ed37a9b2914f86775d4' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/934954de-f1ccd3bb.patch.sql b/include/upgrader/streams/core/934954de-f1ccd3bb.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..c6bb713e7cafd57b4ccc58bd8df41ddc23d9fe0c --- /dev/null +++ b/include/upgrader/streams/core/934954de-f1ccd3bb.patch.sql @@ -0,0 +1,23 @@ +/** + * @version v1.8.1 + * @signature f1ccd3bb620e314b0ae1dbd0a1a99177 + * @title Pluggable Storage Backends + * + * This patch will allow attachments to be stored outside the database (like + * on the filesystem) + */ + +ALTER TABLE `%TABLE_PREFIX%file` + ADD `bk` CHAR(1) NOT NULL DEFAULT 'D' AFTER `ft`, + -- RFC 4288, Section 4.2 declares max MIMEType at 255 ascii chars + CHANGE `type` `type` varchar(255) collate ascii_general_ci NOT NULL default '', + CHANGE `size` `size` BIGINT(20) NOT NULL DEFAULT 0, + CHANGE `hash` `key` VARCHAR(86) COLLATE ascii_general_ci, + ADD `signature` VARCHAR(86) COLLATE ascii_bin AFTER `key`, + ADD `attrs` VARCHAR(255) AFTER `name`, + ADD INDEX (`signature`); + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = 'f1ccd3bb620e314b0ae1dbd0a1a99177' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/ed60ba20-934954de.patch.sql b/include/upgrader/streams/core/ed60ba20-934954de.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..39c210f4d1d7d9ffa65150243cb4cf81db96d552 --- /dev/null +++ b/include/upgrader/streams/core/ed60ba20-934954de.patch.sql @@ -0,0 +1,51 @@ +/** + * @version v1.8.1 + * @signature 934954de8914d9bd2bb8343e805340ae + * @title Various schema improvements and bug fixes + * + */ + +-- [#317](https://github.com/osTicket/osTicket-1.8/issues/317) +ALTER TABLE `%TABLE_PREFIX%faq` + CHANGE `created` `created` datetime NOT NULL, + CHANGE `updated` `updated` datetime NOT NULL; + +-- [#328](https://github.com/osTicket/osTicket-1.8/issues/328) +UPDATE `%TABLE_PREFIX%filter_rule` + SET `how` = 'equal' WHERE `how` IS NULL; + +-- [#331](https://github.com/osTicket/osTicket-1.8/issues/331) +-- Previously there was no primary key on the %ticket_email_info table, so +-- clean up any junk records before adding one +ALTER TABLE `%TABLE_PREFIX%ticket_email_info` + CHANGE `message_id` `thread_id` int(11) unsigned NOT NULL, + DROP INDEX `message_id`, + ADD INDEX `email_mid` (`email_mid`); + +-- [#386](https://github.com/osTicket/osTicket-1.8/issues/386) +UPDATE `%TABLE_PREFIX%email_template` + SET `body` = REPLACE(`body`, '%{recipient}', '%{recipient.name}'); + +-- Change EndUser link to be recipient specific +UPDATE `%TABLE_PREFIX%email_template` + SET `body` = REPLACE(`body`, '%{ticket.client_link}', '%{recipient.ticket_link}'); + +-- Add inline flag and drop ref_type +ALTER TABLE `%TABLE_PREFIX%ticket_attachment` + ADD `inline` tinyint(1) NOT NULL default 0 AFTER `ref_id`, + DROP `ref_type`; + +ALTER TABLE `%TABLE_PREFIX%ticket_thread` + ADD `user_id` int(11) unsigned not null default 0 AFTER `staff_id`; + +ALTER TABLE `%TABLE_PREFIX%ticket` + ADD `email_id` int(11) unsigned not null default 0 AFTER `team_id`, + CHANGE `ticketID` `number` varchar(20); + +ALTER TABLE `%TABLE_PREFIX%ticket_collaborator` + ADD`created` datetime NOT NULL AFTER `role`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = '934954de8914d9bd2bb8343e805340ae' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/f1ccd3bb-f5692e24.cleanup.sql b/include/upgrader/streams/core/f1ccd3bb-f5692e24.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..eec7c2a2a9bade46f2f07a58aa2f11a4f2a8e3e9 --- /dev/null +++ b/include/upgrader/streams/core/f1ccd3bb-f5692e24.cleanup.sql @@ -0,0 +1,21 @@ +/** + * @version v1.8.1 + * @signature f5692e24c7afba7ab6168dde0b3bb3c8 + * @title Add regex field to ticket filters + * + * This fixes a glitch introduced @934954de8914d9bd2bb8343e805340ae where + * a primary key was added to the %ticket_email_info table so that deleting + * can be supported in a clustered environment. The patch added the + * `thread_id` column as the primary key, which was incorrect, because the + * `thread_id` may be null when rejected emails are recorded so they are + * never considered again if found in the inbox. + */ + +-- Add the primary key. The PK on `thread_id` would have been removed in the +-- task if it existed +ALTER TABLE `%TABLE_PREFIX%ticket_email_info` + ADD `id` int(11) unsigned not null auto_increment FIRST, + ADD PRIMARY KEY (`id`); + +-- Drop the CDATA table, if any +DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket__cdata`; diff --git a/include/upgrader/streams/core/f1ccd3bb-f5692e24.patch.sql b/include/upgrader/streams/core/f1ccd3bb-f5692e24.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..fab41f1bc758901bfc0a7537643556a1a90dadb9 --- /dev/null +++ b/include/upgrader/streams/core/f1ccd3bb-f5692e24.patch.sql @@ -0,0 +1,46 @@ +/** + * @version v1.8.1 + * @signature f5692e24c7afba7ab6168dde0b3bb3c8 + * @title Add regex field to ticket filters + * + * This fixes a glitch introduced @934954de8914d9bd2bb8343e805340ae where + * a primary key was added to the %ticket_email_info table so that deleting + * can be supported in a clustered environment. The patch added the + * `thread_id` column as the primary key, which was incorrect, because the + * `thread_id` may be null when rejected emails are recorded so they are + * never considered again if found in the inbox. + */ + +-- [#479](https://github.com/osTicket/osTicket-1.8/issues/479) +-- Add (not)_match to the filter_rule `how` +ALTER TABLE `%TABLE_PREFIX%filter_rule` + CHANGE `how` `how` enum('equal','not_equal','contains','dn_contain','starts','ends','match','not_match') + NOT NULL; + +-- Allow `isactive` to be `-1` for collaborators, which might indicate +-- something like 'unsubscribed' +ALTER TABLE `%TABLE_PREFIX%ticket_collaborator` + CHANGE `isactive` `isactive` tinyint(1) NOT NULL DEFAULT '1'; + +-- There is no `subject` available in the filter::apply method for anything but email +UPDATE `%TABLE_PREFIX%filter_rule` + SET `what` = CONCAT('field.', ( + SELECT field.`id` FROM `%TABLE_PREFIX%form_field` field + JOIN `%TABLE_PREFIX%form` form ON (field.form_id = form.id) + WHERE field.`name` = 'subject' AND form.`type` = 'T' + )) + WHERE `what` = 'subject'; + +-- There is no `body` available in the filter::apply method for anything but emails +UPDATE `%TABLE_PREFIX%filter_rule` + SET `what` = CONCAT('field.', ( + SELECT field.`id` FROM `%TABLE_PREFIX%form_field` field + JOIN `%TABLE_PREFIX%form` form ON (field.form_id = form.id) + WHERE field.`name` = 'message' AND form.`type` = 'T' + )) + WHERE `what` = 'body'; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = 'f5692e24c7afba7ab6168dde0b3bb3c8' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/f1ccd3bb-f5692e24.task.php b/include/upgrader/streams/core/f1ccd3bb-f5692e24.task.php new file mode 100644 index 0000000000000000000000000000000000000000..fd58fe77064f1a81e41e6dc7a31ba0daac3a1de9 --- /dev/null +++ b/include/upgrader/streams/core/f1ccd3bb-f5692e24.task.php @@ -0,0 +1,27 @@ +<?php + +/* + * Drops the `thread_id` primary key on the ticket_email_info table if it + * exists + */ + +class DropTicketEmailInfoPk extends MigrationTask { + var $description = "Reticulating splines"; + + function run($max_time) { + $sql = 'SELECT `INDEX_NAME` FROM information_schema.statistics + WHERE table_schema = '.db_input(DBNAME) + .' AND table_name = '.db_input(TICKET_EMAIL_INFO_TABLE) + .' AND column_name = '.db_input('thread_id'); + if ($name = db_result(db_query($sql))) { + if ($name == 'PRIMARY') { + db_query('ALTER TABLE `'.TICKET_EMAIL_INFO_TABLE + .'` DROP PRIMARY KEY'); + } + } + } +} + +return 'DropTicketEmailInfoPk'; + +?> diff --git a/js/osticket.js b/js/osticket.js index e38a51ef26f1ab74e0f61a59f1f96b9f4f1b3660..f7d956e51202ebfec06a7d65ed0e1907c8e29415 100644 --- a/js/osticket.js +++ b/js/osticket.js @@ -131,7 +131,7 @@ showImagesInline = function(urls, thread_id) { ? '.thread-body img[data-cid]' : '.thread-body#thread-id-'+thread_id+' img[data-cid]'; $(selector).each(function(i, el) { - var cid = $(el).data('cid'), + var cid = $(el).data('cid').toLowerCase(), info = urls[cid], e = $(el); if (info) { diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index 09629a041ad1e60d9a4f5988210aa56564aea6c2..d41c87ba68370adcbf36ae21f7a167025d72d0a7 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -119,6 +119,71 @@ RedactorPlugins.draft = { } }; +RedactorPlugins.signature = { + init: function() { + var $el = $(this.$element.get(0)), + inner = $('<div class="inner"></div>'); + if ($el.data('signatureField')) { + this.$signatureBox = $('<div class="selected-signature"></div>') + .append(inner) + .appendTo(this.$box); + if ($el.data('signature')) + inner.html($el.data('signature')); + else + this.$signatureBox.hide(); + $('input[name='+$el.data('signatureField')+']', $el.closest('form')) + .on('change', false, false, $.proxy(this.updateSignature, this)) + if ($el.data('deptField')) + $(':input[name='+$el.data('deptField')+']', $el.closest('form')) + .on('change', false, false, $.proxy(this.updateSignature, this)) + // Expand on hover + var outer = this.$signatureBox, + inner = $('.inner', this.$signatureBox).get(0), + originalHeight = outer.height(), + hoverTimeout = undefined, + originalShadow = this.$signatureBox.css('box-shadow'); + this.$signatureBox.hover(function() { + hoverTimeout = setTimeout($.proxy(function() { + originalHeight = Math.max(originalHeight, outer.height()); + $(this).animate({ + 'height': inner.offsetHeight + }, 'fast'); + $(this).css('box-shadow', 'none', 'important'); + }, this), 250); + }, function() { + clearTimeout(hoverTimeout); + $(this).stop().animate({ + 'height': Math.min(inner.offsetHeight, originalHeight) + }, 'fast'); + $(this).css('box-shadow', originalShadow); + }); + } + }, + updateSignature: function(e) { + var $el = $(this.$element.get(0)); + selected = $(':input:checked[name='+$el.data('signatureField')+']', $el.closest('form')).val(), + type = $(e.target).val(), + dept = $(':input[name='+$el.data('deptField')+']', $el.closest('form')).val(), + url = 'ajax.php/content/signature/', + inner = $('.inner', this.$signatureBox); + e.preventDefault && e.preventDefault(); + if (selected == 'dept' && $el.data('deptId')) + url += 'dept/' + $el.data('deptId'); + else if (selected == 'dept' && $el.data('deptField')) { + if (dept) + url += 'dept/' + dept + else + return inner.empty().parent().hide(); + } + else if (type == 'none') + return inner.empty().parent().hide(); + else + url += selected + + inner.load(url).parent().show(); + } +}; + /* Redactor richtext init */ $(function() { var captureImageSizes = function(html) { @@ -150,7 +215,7 @@ $(function() { 'autoresize': !el.hasClass('no-bar'), 'minHeight': el.hasClass('small') ? 75 : 150, 'focus': false, - 'plugins': ['fontcolor','fontfamily'], + 'plugins': ['fontcolor','fontfamily', 'signature'], 'imageGetJson': 'ajax.php/draft/images/browse', 'syncBeforeCallback': captureImageSizes, 'linebreaks': true, diff --git a/kb/file.php b/kb/file.php index c411002b8f1ed2f2674f7eb52528dc9355570891..21336765817fa588a82a983af5e8b52dc8da2a85 100644 --- a/kb/file.php +++ b/kb/file.php @@ -23,7 +23,7 @@ $h=trim($_GET['h']); //basic checks if(!$h || strlen($h)!=64 //32*2 || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash. - || strcasecmp(substr($h,-32),md5($file->getId().session_id().$file->getHash()))) //next 32 is file id + session hash. + || strcasecmp(substr($h,-32),md5($file->getId().session_id().$file->getKey()))) //next 32 is file id + session hash. die('Unknown or invalid file. #'.Format::htmlchars($_GET['h'])); $file->download(); diff --git a/login.php b/login.php index 789938980ff451cdcc72920d2b9f88080dcc7a47..9e6d5f36f18d64384f96818ae82498a774553344 100644 --- a/login.php +++ b/login.php @@ -2,7 +2,10 @@ /********************************************************************* login.php - Client Login + User access link recovery + + TODO: This is a temp. fix to allow for collaboration in lieu of real + username and password coming in 1.8.2 Peter Rotich <peter@osticket.com> Copyright (c) 2006-2013 osTicket @@ -21,21 +24,26 @@ define('OSTCLIENTINC',TRUE); //make includes happy require_once(INCLUDE_DIR.'class.client.php'); require_once(INCLUDE_DIR.'class.ticket.php'); -if($_POST) { - - if(($user=Client::login(trim($_POST['lticket']), trim($_POST['lemail']), null, $errors))) { - //XXX: Ticket owner is assumed. - @header('Location: tickets.php?id='.$user->getTicketID()); - require_once('tickets.php'); //Just in case of 'header already sent' error. - exit; +$inc = 'login.inc.php'; +if ($_POST) { + if (!$_POST['lticket'] || !Validator::is_email($_POST['lemail'])) + $errors['err'] = 'Valid email address and ticket number required'; + elseif (($user = UserAuthenticationBackend::process($_POST['lemail'], + $_POST['lticket'], $errors))) { + //We're using authentication backend so we can guard aganist brute + // force attempts (which doesn't buy much since the link is emailed) + $user->sendAccessLink(); + $msg = sprintf("%s - access link sent to your email!", + $user->getName()->getFirst()); + $_POST = null; } elseif(!$errors['err']) { - $errors['err'] = 'Authentication error - try again!'; + $errors['err'] = 'Invalid email or ticket number - try again!'; } } $nav = new UserNav(); $nav->setActiveNav('status'); -require(CLIENTINC_DIR.'header.inc.php'); -require(CLIENTINC_DIR.'login.inc.php'); -require(CLIENTINC_DIR.'footer.inc.php'); +require CLIENTINC_DIR.'header.inc.php'; +require CLIENTINC_DIR.$inc; +require CLIENTINC_DIR.'footer.inc.php'; ?> diff --git a/logout.php b/logout.php index 6c6482d9c8c6fe4e452c6a0494abdcba8ae01a93..4b9ea91b133fae4adf96823b8037428d1ed5acd0 100644 --- a/logout.php +++ b/logout.php @@ -16,12 +16,10 @@ require('client.inc.php'); //Check token: Make sure the user actually clicked on the link to logout. -if(!$_GET['auth'] || !$ost->validateLinkToken($_GET['auth'])) +if(!$thisclient || !$_GET['auth'] || !$ost->validateLinkToken($_GET['auth'])) @header('Location: index.php'); -$_SESSION['_client']=array(); -session_unset(); -session_destroy(); +$thisclient->logOut(); header('Location: index.php'); require('index.php'); ?> diff --git a/open.php b/open.php index a731e2132465e29d0014ae78341d18fd0614408d..4cb7684a8167b7cd5a3c07f67d876b83c313cb4d 100644 --- a/open.php +++ b/open.php @@ -20,8 +20,8 @@ $errors=array(); if($_POST): $vars = $_POST; $vars['deptId']=$vars['emailId']=0; //Just Making sure we don't accept crap...only topicId is expected. - if($thisclient) { - $vars['uid'] = $thisclient->getUserId(); + if ($thisclient) { + $vars['uid']=$thisclient->getId(); } elseif($cfg->isCaptchaEnabled()) { if(!$_POST['captcha']) $errors['captcha']='Enter text shown on the image'; @@ -53,11 +53,9 @@ if($_POST): } //Logged in...simply view the newly created ticket. if($thisclient && $thisclient->isValid()) { - if(!$cfg->showRelatedTickets()) - $_SESSION['_client']['key']= $ticket->getExtId(); //Resetting login Key to the current ticket! session_write_close(); session_regenerate_id(); - @header('Location: tickets.php?id='.$ticket->getExtId()); + @header('Location: tickets.php?id='.$ticket->getId()); } }else{ $errors['err']=$errors['err']?$errors['err']:'Unable to create a ticket. Please correct errors below and try again!'; diff --git a/profile.php b/profile.php new file mode 100644 index 0000000000000000000000000000000000000000..e17bf690eb9241ff2b7bbad74efe8684bed447d2 --- /dev/null +++ b/profile.php @@ -0,0 +1,35 @@ +<?php +/********************************************************************* + profile.php + + Manage client profile. This will allow a logged-in user to manage + his/her own public (non-internal) information + + Peter Rotich <peter@osticket.com> + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: + $Id: $ +**********************************************************************/ +require 'secure.inc.php'; + +require_once 'class.user.php'; +$user = User::lookup($thisclient->getId()); + +if ($user && $_POST) { + $errors = array(); + if ($user->updateInfo($_POST, $errors)) + Http::redirect('tickets.php'); +} + +$inc = 'profile.inc.php'; + +include(CLIENTINC_DIR.'header.inc.php'); +include(CLIENTINC_DIR.$inc); +include(CLIENTINC_DIR.'footer.inc.php'); + diff --git a/scp/ajax.php b/scp/ajax.php index a5e56bd409c67381fcf2461dc627723d7a92460f..5bc266064fda0a468c766d093371efffa411d3c5 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -41,7 +41,8 @@ $dispatcher = patterns('', )), url('^/content/', patterns('ajax.content.php:ContentAjaxAPI', url_get('^log/(?P<id>\d+)', 'log'), - url_get('^ticket_variables', 'ticket_variables') + url_get('^ticket_variables', 'ticket_variables'), + url_get('^signature/(?P<type>\w+)(?:/(?P<id>\d+))?$', 'getSignature') )), url('^/config/', patterns('ajax.config.php:ConfigAjaxAPI', url_get('^scp', 'scp') @@ -63,11 +64,13 @@ $dispatcher = patterns('', url_get('^/(?P<id>\d+)$', 'getUser'), url_post('^/(?P<id>\d+)$', 'updateUser'), url_get('^/(?P<id>\d+)/edit$', 'editUser'), - url_get('^/lookup$', 'getUser'), + url('^/lookup$', 'getUser'), url_get('^/lookup/form$', 'getLookupForm'), url_post('^/lookup/form$', 'addUser'), url_get('^/select$', 'selectUser'), - url_get('^/select/(?P<id>\d+)$', 'selectUser') + url_get('^/select/(?P<id>\d+)$', 'selectUser'), + url_get('^/select/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteUser'), + url_get('^/staff$', 'searchStaff') )), url('^/tickets/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<tid>\d+)/change-user$', 'changeUserForm'), @@ -78,9 +81,19 @@ $dispatcher = patterns('', url_post('^(?P<tid>\d+)/lock$', 'acquireLock'), url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/renew', 'renewLock'), url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/release', 'releaseLock'), + url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'), + url_get('^(?P<tid>\d+)/collaborators$', 'showCollaborators'), + url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'), + url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'), + url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'), + url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'), url_get('^lookup', 'lookup'), url_get('^search', 'search') )), + url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', + url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), + url_post('^(?P<cid>\d+)$', 'updateCollaborator') + )), url('^/draft/', patterns('ajax.draft.php:DraftAjaxAPI', url_post('^(?P<id>\d+)$', 'updateDraft'), url_delete('^(?P<id>\d+)$', 'deleteDraft'), @@ -91,11 +104,13 @@ $dispatcher = patterns('', )), url_post('^/upgrader', array('ajax.upgrader.php:UpgraderAjaxAPI', 'upgrade')), url('^/help/', patterns('ajax.tips.php:HelpTipAjaxAPI', - url_get('tips/(?P<namespace>[\w_.]+)$', 'getTipsJson'), - url_get('(?P<lang>\w{2}_\w{2})?/tips/(?P<namespace>[\w_.]+)$', 'getTipsForLangJson') + url_get('^tips/(?P<namespace>[\w_.]+)$', 'getTipsJson'), + url_get('^(?P<lang>[\w_]+)?/tips/(?P<namespace>[\w_.]+)$', 'getTipsJsonForLang') )) ); +Signal::send('ajax.scp', $dispatcher); + # Call the respective function print $dispatcher->resolve($ost->get_path_info()); ?> diff --git a/scp/attachment.php b/scp/attachment.php index 44a49d61bd264edef8e54b1c107126a11964aeed..28b9a185b4df488674d9157a13292cefb39bbc60 100644 --- a/scp/attachment.php +++ b/scp/attachment.php @@ -23,7 +23,7 @@ if(!$thisstaff || !$_GET['id'] || !$_GET['h'] die('Unknown attachment!'); //Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!! -$vhash=md5($attachment->getFileId().session_id().$file->getHash()); +$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey())); if(strcasecmp(trim($_GET['h']),$vhash) || !($ticket=$attachment->getTicket()) || !$ticket->checkStaffAccess($thisstaff)) die('Access Denied'); //Download the file.. diff --git a/scp/css/scp.css b/scp/css/scp.css index 64670cecb6b52e0721fac8f03125c023a074c022..ea1a1dddeec181a5c63271437c39f00925068394 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -842,23 +842,29 @@ h2 .reload { padding:0 10px; } -#response_options ul.tabs { - padding:4px 0 0 190px; +ul.tabs { + padding:4px 0 0 20px; margin:0; + margin-bottom: 5px; text-align:center; height:29px; border-bottom:1px solid #aaa; background:#eef3f8; } -#response_options ul.tabs li { +#response_options ul.tabs { + padding-left:190px; +} + + +ul.tabs li { margin:0; padding:0; display:inline; list-style:none; } -#response_options ul.tabs li a { +ul.tabs li a { width:130px; font-weight:bold; padding:5px; @@ -882,7 +888,7 @@ h2 .reload { background-repeat:no-repeat; } -#response_options ul.tabs li a.active { +ul.tabs li a.active { height:18px; color:#184E81; background-color:#f9f9f9; @@ -946,7 +952,7 @@ h2 .reload { display:block; height:30px; position:absolute; - z-index:1000; + z-index:3; } .tip_arrow { @@ -955,7 +961,7 @@ h2 .reload { top:5px; left:-12px; width:12px; - z-index:700; + z-index:4; } .tip_box.right .tip_arrow { @@ -986,7 +992,7 @@ h2 .reload { -moz-box-shadow: 3px 3px 3px #666; -webkit-box-shadow: 3px 3px 3px #666; box-shadow: 3px 3px 3px #666; - z-index:500; + z-index:3; position:absolute; top:0; left:-1px; @@ -1256,7 +1262,7 @@ time { background:#f8f8f8; border:2px solid #2a67ac; display:none; - z-index:5; + z-index:6; box-shadow: 0 5px 60px #001; border-radius: 5px; max-height: 72%; @@ -1264,7 +1270,7 @@ time { } .redactor_air { - z-index: 6 !important; + z-index: 7 !important; } .dialog#advanced-search { @@ -1479,7 +1485,7 @@ ul.progress li.no small {color:red;} width: 100%; height: 100%; background: #000; - z-index: 1; + z-index: 5; -webkit-transform: translate3d(0,0,0); } @@ -1528,3 +1534,17 @@ div.patch { .patch-title { color: #555; } + +div.selected-signature { + border-top: 1px dotted #aaa; + padding: 0.3em 10px 5px; + background-color: #f9f9f9; + height: 2.5em; + overflow-y: hidden; + box-shadow: inset 0px -5px 5px -5px rgba(206, 199, 182, 1.0); + font-size: 15px; + line-height: 1.25rem; +} +div.selected-signature .inner { + opacity: 0.5; +} diff --git a/scp/emailtest.php b/scp/emailtest.php index 6a0787e377362ec8f3dd2eb2b9ae1614174856f9..83a896020fde2760d30618f23e96c0d17b27618d 100644 --- a/scp/emailtest.php +++ b/scp/emailtest.php @@ -36,7 +36,8 @@ if($_POST){ if(!$errors && $email){ if($email->send($_POST['email'],$_POST['subj'], - Format::sanitize($_POST['message']))) { + Format::sanitize($_POST['message']), + null, array('reply-tag'=>false))) { $msg='Test email sent successfully to '.Format::htmlchars($_POST['email']); Draft::deleteForNamespace('email.diag'); } diff --git a/scp/file.php b/scp/file.php index c02562eb2a2fb1fe334884f5bd7f66f99800f181..9d6518d0ae4f4d53656389503bb83c71682a8963 100644 --- a/scp/file.php +++ b/scp/file.php @@ -23,7 +23,7 @@ $h=trim($_GET['h']); //basic checks if(!$h || strlen($h)!=64 //32*2 || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash. - || strcasecmp(substr($h,-32),md5($file->getId().session_id().$file->getHash()))) //next 32 is file id + session hash. + || $file->getDownloadHash() != $h) //next 32 is file id + session hash. die('Unknown or invalid file. #'.Format::htmlchars($_GET['h'])); $file->download(); diff --git a/scp/js/scp.js b/scp/js/scp.js index a243d7cb4f33bc79fbeb033d6e4774fa2a3fdc60..33839e80c39174391d9d10e9e2c2029a44af9221 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -313,9 +313,11 @@ $(document).ready(function(){ }); /* Typeahead tickets lookup */ + var last_req; $('#basic-ticket-search').typeahead({ source: function (typeahead, query) { - $.ajax({ + if (last_req) last_req.abort(); + last_req = $.ajax({ url: "ajax.php/tickets/lookup?q="+query, dataType: 'json', success: function (data) { @@ -334,7 +336,8 @@ $(document).ready(function(){ $('.email.typeahead').typeahead({ source: function (typeahead, query) { if(query.length > 2) { - $.ajax({ + if (last_req) last_req.abort(); + last_req = $.ajax({ url: "ajax.php/users?q="+query, dataType: 'json', success: function (data) { @@ -350,6 +353,27 @@ $(document).ready(function(){ }, property: "email" }); + $('.staff-username.typeahead').typeahead({ + source: function (typeahead, query) { + if(query.length > 2) { + if (last_req) last_req.abort(); + last_req = $.ajax({ + url: "ajax.php/users/staff?q="+query, + dataType: 'json', + success: function (data) { + typeahead.process(data); + } + }); + } + }, + onselect: function (obj) { + var fObj=$('.staff-username.typeahead').closest('form'); + $.each(['first','last','email','phone','mobile'], function(i,k) { + if (obj[k]) $('.auto.'+k, fObj).val(obj[k]); + }); + }, + property: "username" + }); //Overlay $('#overlay').css({ @@ -407,13 +431,17 @@ $(document).ready(function(){ $('#advanced-search').show(); }); - $.userLookup = function (url, callback) { + $.dialog = function (url, code, cb, options) { + options = options||{}; $('.dialog#popup .body').load(url, function () { $('#overlay').show(); - $('.dialog#popup').show(); - $(document).off('.user'); - $(document).on('submit.user', '.dialog#popup form.user',function(e) { + $('.dialog#popup').show({ + duration: 0, + complete: function() { if (options.onshow) options.onshow(); } + }); + $(document).off('.dialog'); + $(document).on('submit.dialog', '.dialog#popup form', function(e) { e.preventDefault(); var $form = $(this); var $dialog = $form.closest('.dialog'); @@ -423,12 +451,11 @@ $(document).ready(function(){ data: $form.serialize(), cache: false, success: function(resp, status, xhr) { - if (xhr && xhr.status == 201) { - var user = $.parseJSON(xhr.responseText); + if (xhr && xhr.status == code) { $('div.body', $dialog).empty(); $dialog.hide(); $('#overlay').hide(); - if(callback) callback(user); + if(cb) cb(xhr.responseText); } else { $('div.body', $dialog).html(resp); $('#msg_notice, #msg_error', $dialog).delay(5000).slideUp(); @@ -440,8 +467,18 @@ $(document).ready(function(){ return false; }); }); + if (options.onload) { options.onload(); } }; + $.userLookup = function (url, cb) { + $.dialog(url, 201, function (resp) { + var user = $.parseJSON(resp); + if(cb) cb(user); + }, { + onshow: function() { $('#user-search').focus(); } + }); + }; + $('#advanced-search').delegate('#status', 'change', function() { switch($(this).val()) { case 'closed': @@ -516,6 +553,31 @@ $(document).ready(function(){ }); } }); + + //Tabs + $(document).on('click.tab', 'ul.tabs li a', function(e) { + e.preventDefault(); + if ($('.tab_content'+$(this).attr('href')).length) { + $('ul.tabs li a').removeClass('active'); + $(this).addClass('active'); + $('.tab_content').hide(); + $('.tab_content'+$(this).attr('href')).show(); + } + }); + + //Collaborators + $(document).on('click', 'a.collaborator, a.collaborators', function(e) { + e.preventDefault(); + var url = 'ajax.php/'+$(this).attr('href').substr(1); + $.dialog(url, 201, function (resp) { + $('input#emailcollab').show(); + $('#recipients').text(resp); + $('.tip_box').remove(); + }, { + onshow: function() { $('#user-search').focus(); } + }); + return false; + }); }); // NOTE: getConfig should be global diff --git a/scp/js/ticket.js b/scp/js/ticket.js index 9ddc245e3a2a8754b604bc9a4874fb7ea9606cdf..357031c9e00f4dd506c1d93346f7c0bea3e901df 100644 --- a/scp/js/ticket.js +++ b/scp/js/ticket.js @@ -363,6 +363,14 @@ jQuery(function($) { return false; }); + $(document).on('change', 'form#reply select#emailreply', function(e) { + var $cc = $('form#reply tbody#cc_sec'); + if($(this).val() == 0) + $cc.hide(); + else + $cc.show(); + }); + var showNonLocalImage = function(div) { var $div = $(div), $img = $div.append($('<img>') @@ -425,13 +433,13 @@ showImagesInline = function(urls, thread_id) { ? '.thread-body img[data-cid]' : '.thread-body#thread-id-'+thread_id+' img[data-cid]'; $(selector).each(function(i, el) { - var cid = $(el).data('cid'), + var cid = $(el).data('cid').toLowerCase(), info = urls[cid], e = $(el); if (info) { // Add a hover effect with the filename var timeout, caption = $('<div class="image-hover">') - .css('float',e.css('float')); + .css({'float':e.css('float')}); e.wrap(caption).parent() .hover( function() { diff --git a/scp/js/tips.js b/scp/js/tips.js index 6d7a3c051d19a5d100e1ea24210c64bfe118e3ef..9fdd2261701abc3c60012dd54a6ebe10a6b548fc 100644 --- a/scp/js/tips.js +++ b/scp/js/tips.js @@ -160,6 +160,28 @@ jQuery(function() { clearTimeout($(this).data('timer')); }); + + $('a.collaborators.preview').live('mouseover', function(e) { + e.preventDefault(); + var elem = $(this); + + var url = 'ajax.php/'+elem.attr('href').substr(1)+'/preview'; + var xoffset = 100; + elem.data('timer', 0); + + if (e.type=='mouseover') { + elem.data('timer',setTimeout(function() { showtip(url, elem, xoffset);},750)) + } else { + showtip(url,elem,xoffset); + } + }).live('mouseout', function(e) { + clearTimeout($(this).data('timer')); + }).live('click', function(e) { + clearTimeout($(this).data('timer')); + $('.tip_box').remove(); + }); + + //Ticket preview $('.ticketPreview').live('mouseover', function(e) { e.preventDefault(); @@ -170,18 +192,19 @@ jQuery(function() { var id='t'+vars[1]; var xoffset = 80; - - elem.data('id',id); - elem.data('timer',0); - if($('.' + id).length == 0) { + elem.data('timer', 0); + if(!elem.data('id')) { + elem.data('id', id); if(e.type=='mouseover') { /* wait about 1 sec - before showing the tip - mouseout kills the timeout*/ elem.data('timer',setTimeout(function() { showtip(url,elem,xoffset);},750)) }else{ + clearTimeout(elem.data('timer')); showtip(url,elem,xoffset); } } }).live('mouseout', function(e) { + $(this).data('id', 0); clearTimeout($(this).data('timer')); }); diff --git a/scp/lists.php b/scp/lists.php index 3fa3e3565b0643b5e123d5d8baff2f5593e5cff5..a68267703661f332df7f8b5f4ad4d973b59f60cd 100644 --- a/scp/lists.php +++ b/scp/lists.php @@ -8,12 +8,22 @@ if($_REQUEST['id'] && !($list=DynamicList::lookup($_REQUEST['id']))) if($_POST) { $fields = array('name', 'name_plural', 'sort_mode', 'notes'); + $required = array('name'); switch(strtolower($_POST['do'])) { case 'update': foreach ($fields as $f) - if (isset($_POST[$f])) + if (in_array($f, $required) && !$_POST[$f]) + $errors[$f] = sprintf('%s is required', + mb_convert_case($f, MB_CASE_TITLE)); + elseif (isset($_POST[$f])) $list->set($f, $_POST[$f]); - $list->save(true); + if ($errors) + $errors['err'] = 'Unable to update custom list. Correct any error(s) below and try again.'; + elseif ($list->save(true)) + $msg = 'Custom list updated successfully'; + else + $errors['err'] = 'Unable to update custom list. Unknown internal error'; + foreach ($list->getItems() as $item) { $id = $item->get('id'); if ($_POST["delete-$id"] == 'on') { @@ -27,12 +37,23 @@ if($_POST) { } break; case 'add': + foreach ($fields as $f) + if (in_array($f, $required) && !$_POST[$f]) + $errors[$f] = sprintf('%s is required', + mb_convert_case($f, MB_CASE_TITLE)); $list = DynamicList::create(array( 'name'=>$_POST['name'], 'name_plural'=>$_POST['name_plural'], 'sort_mode'=>$_POST['sort_mode'], 'notes'=>$_POST['notes'])); - $list->save(true); + + if ($errors) + $errors['err'] = 'Unable to create custom list. Correct any error(s) below and try again.'; + elseif ($list->save(true)) + $msg = 'Custom list added successfully'; + else + $errors['err'] = 'Unable to create custom list. Unknown internal error'; + break; case 'mass_process': diff --git a/scp/login.php b/scp/login.php index 2f3cf2236e9f4996bb10b94764fb6d0a14d99d22..20f53938e163334af2db7e8f83d2d0e450ac35fa 100644 --- a/scp/login.php +++ b/scp/login.php @@ -22,17 +22,29 @@ require_once(INCLUDE_DIR.'class.csrf.php'); $dest = $_SESSION['_staff']['auth']['dest']; $msg = $_SESSION['_staff']['auth']['msg']; $msg = $msg?$msg:'Authentication Required'; +$dest=($dest && (!strstr($dest,'login.php') && !strstr($dest,'ajax.php')))?$dest:'index.php'; +$show_reset = false; if($_POST) { - //$_SESSION['_staff']=array(); #Uncomment to disable login strikes. - if(($user=Staff::login($_POST['userid'], $_POST['passwd'], $errors))){ - $dest=($dest && (!strstr($dest,'login.php') && !strstr($dest,'ajax.php')))?$dest:'index.php'; - @header("Location: $dest"); + // Lookup support backends for this staff + $username = trim($_POST['userid']); + if ($user = StaffAuthenticationBackend::process($username, + $_POST['passwd'], $errors)) { + session_write_close(); + Http::redirect($dest); require_once('index.php'); //Just incase header is messed up. exit; } $msg = $errors['err']?$errors['err']:'Invalid login'; + $show_reset = true; } +// Consider single sign-on authentication backends +else if (!$thisstaff || !($thisstaff->getId() || $thisstaff->isValid())) { + if (($user = StaffAuthenticationBackend::processSignOn($errors, false)) + && ($user instanceof StaffSession)) + @header("Location: $dest"); +} + define("OSTSCPINC",TRUE); //Make includes happy! include_once(INCLUDE_DIR.'staff/login.tpl.php'); ?> diff --git a/scp/logout.php b/scp/logout.php index 7076dcec4c0984192acab06de20c5a16cba91416..bdc697c78beceb7b4cf3185603f45afbcf8d2838 100644 --- a/scp/logout.php +++ b/scp/logout.php @@ -1,30 +1,36 @@ -<?php -/********************************************************************* - logout.php - - Log out staff - Destroy the session and redirect to login.php - - Peter Rotich <peter@osticket.com> - Copyright (c) 2006-2013 osTicket - http://www.osticket.com - - Released under the GNU General Public License WITHOUT ANY WARRANTY. - See LICENSE.TXT for details. - - vim: expandtab sw=4 ts=4 sts=4: -**********************************************************************/ -require('staff.inc.php'); -//Check token: Make sure the user actually clicked on the link to logout. -if(!$_GET['auth'] || !$ost->validateLinkToken($_GET['auth'])) - @header('Location: index.php'); - -$ost->logDebug('Staff logout', - sprintf("%s logged out [%s]", - $thisstaff->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. -$_SESSION['_staff']=array(); -session_unset(); -session_destroy(); -@header('Location: login.php'); -require('login.php'); -?> +<?php +/********************************************************************* + logout.php + + Log out staff + Destroy the session and redirect to login.php + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require('staff.inc.php'); + +//Check token: Make sure the user actually clicked on the link to logout. +if(!$_GET['auth'] || !$ost->validateLinkToken($_GET['auth'])) + @header('Location: index.php'); + +$thisstaff->logOut(); + +//Clear any ticket locks the staff has. +TicketLock::removeStaffLocks($thisstaff->getId()); + +//Destroy session on logout. +// TODO: Stop doing this starting with 1.9 - separate session data per +// app/panel. +session_unset(); +session_destroy(); + +@header('Location: login.php'); +require('login.php'); +?> diff --git a/scp/plugins.php b/scp/plugins.php new file mode 100644 index 0000000000000000000000000000000000000000..44dda73b80897b668caafe38638c163321a31e64 --- /dev/null +++ b/scp/plugins.php @@ -0,0 +1,62 @@ +<?php +require('admin.inc.php'); +require_once(INCLUDE_DIR."/class.plugin.php"); + +if($_REQUEST['id'] && !($plugin=Plugin::lookup($_REQUEST['id']))) + $errors['err']='Unknown or invalid plugin ID.'; + +if($_POST) { + switch(strtolower($_POST['do'])) { + case 'update': + if ($plugin) { + $plugin->getConfig()->commit($errors); + } + break; + case 'mass_process': + if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { + $errors['err'] = 'You must select at least one plugin'; + } else { + $count = count($_POST['ids']); + switch(strtolower($_POST['a'])) { + case 'enable': + foreach ($_POST['ids'] as $id) { + if ($p = Plugin::lookup($id)) { + $p->enable(); + } + } + break; + case 'disable': + foreach ($_POST['ids'] as $id) { + if ($p = Plugin::lookup($id)) { + $p->disable(); + } + } + break; + case 'delete': + foreach ($_POST['ids'] as $id) { + if ($p = Plugin::lookup($id)) { + $p->uninstall(); + } + } + break; + } + } + break; + case 'install': + if ($ost->plugins->install($_POST['install_path'])) + $msg = 'Plugin successfully installed'; + break; + } +} + +$page = 'plugins.inc.php'; +if ($plugin) + $page = 'plugin.inc.php'; +elseif ($_REQUEST['a']=='add') + $page = 'plugin-add.inc.php'; + +$nav->setTabActive('manage'); +require(STAFFINC_DIR.'header.inc.php'); +require(STAFFINC_DIR.$page); +include(STAFFINC_DIR.'footer.inc.php'); +?> diff --git a/scp/pwreset.php b/scp/pwreset.php index 5b7a20fa86ab35ce1c9e701107a8a9e8be93dee0..b2826014c9b46b27572d3de6f3c6801c89183a6d 100644 --- a/scp/pwreset.php +++ b/scp/pwreset.php @@ -36,7 +36,10 @@ if($_POST) { switch ($_POST['do']) { case 'sendmail': if (($staff=Staff::lookup($_POST['userid']))) { - if (!$staff->sendResetEmail()) { + if (!$staff->hasPassword()) { + $msg = 'Unable to reset password. Contact your administrator'; + } + elseif (!$staff->sendResetEmail()) { $tpl = 'pwreset.sent.php'; } } @@ -47,25 +50,13 @@ if($_POST) { case 'newpasswd': // TODO: Compare passwords $tpl = 'pwreset.login.php'; - $_config = new Config('pwreset'); - if (($staff = new StaffSession($_POST['userid'])) && - !$staff->getId()) - $msg = 'Invalid user-id given'; - elseif (!($id = $_config->get($_POST['token'])) - || $id != $staff->getId()) - $msg = 'Invalid reset token'; - elseif (!($ts = $_config->lastModified($_POST['token'])) - && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts)))) - $msg = 'Invalid reset token'; - elseif (!$staff->forcePasswdRest()) - $msg = 'Unable to reset password'; - else { + $errors = array(); + if ($staff = StaffAuthenticationBackend::processSignOn($errors)) { $info = array('page' => 'index.php'); - Signal::send('auth.pwreset.login', $staff, $info); - Staff::_do_login($staff, $_POST['userid']); - $_SESSION['_staff']['reset-token'] = $_POST['token']; - header('Location: '.$info['page']); - exit(); + Http::redirect($info['page']); + } + elseif (isset($errors['msg'])) { + $msg = $errors['msg']; } break; } diff --git a/scp/staff.inc.php b/scp/staff.inc.php index 73fe46d3804b449966f0dc58cbda68308a8145fc..e900ab0f89e7a9c0f2f51eab3efda01299746dd0 100644 --- a/scp/staff.inc.php +++ b/scp/staff.inc.php @@ -57,14 +57,13 @@ if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the fu } } -$thisstaff = new StaffSession($_SESSION['_staff']['userID']); //Set staff object. +$thisstaff = StaffAuthenticationBackend::getUser(); //1) is the user Logged in for real && is staff. -if(!$thisstaff->getId() || !$thisstaff->isValid()){ +if (!$thisstaff || !$thisstaff->getId() || !$thisstaff->isValid()) { if (isset($_SESSION['_staff']['auth']['msg'])) { $msg = $_SESSION['_staff']['auth']['msg']; unset($_SESSION['_staff']['auth']['msg']); - } - elseif (isset($_SESSION['_staff']['userID']) && !$thisstaff->isValid()) + } elseif ($thisstaff && !$thisstaff->isValid()) $msg = 'Session timed out due to inactivity'; else $msg = 'Authentication Required'; diff --git a/scp/tickets.php b/scp/tickets.php index 228db38d67a117e01eefbf8bbaf8b3c888955899..f679f5035a2355ef80482f4a8084f09975c25427 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -71,7 +71,7 @@ if($_POST && !$errors): if(!$errors && $_FILES['attachments']) $vars['files'] = AttachmentFile::format($_FILES['attachments']); - if(!$errors && ($response=$ticket->postReply($vars, $errors, isset($_POST['emailreply'])))) { + if(!$errors && ($response=$ticket->postReply($vars, $errors, $_POST['emailreply']))) { $msg='Reply posted successfully'; $ticket->reload(); @@ -222,7 +222,7 @@ if($_POST && !$errors): } elseif($ticket->isClosed()) { $errors['err'] = 'Ticket is already closed!'; } elseif($ticket->close()) { - $msg='Ticket #'.$ticket->getExtId().' status set to CLOSED'; + $msg='Ticket #'.$ticket->getNumber().' status set to CLOSED'; //Log internal note if($_POST['ticket_status_notes']) $note = $_POST['ticket_status_notes']; diff --git a/setup/ajax.php b/setup/ajax.php index 9c2c7b282a325f135f3dde4e8e80491860666b31..97e45daddc88ece9cd65e24ea71dfb9f0d7ac8b9 100644 --- a/setup/ajax.php +++ b/setup/ajax.php @@ -23,8 +23,8 @@ require_once INCLUDE_DIR.'/class.ajax.php'; $dispatcher = patterns('', url('^/help/', patterns('ajax.tips.php:HelpTipAjaxAPI', - url_get('tips/(?P<namespace>[\w_]+)$', 'getTipsJson'), - url_get('(?P<lang>\w{2}_\w{2})?/tips/(?P<namespace>[\w_]+)$', 'getTipsForLangJson') + url_get('^tips/(?P<namespace>[\w_.]+)$', 'getTipsJson'), + url_get('^(?P<lang>[\w_]+)?/tips/(?P<namespace>[\w_.]+)$', 'getTipsJsonForLang') )) ); print $dispatcher->resolve(Osticket::get_path_info()); diff --git a/setup/cli/cli.inc.php b/setup/cli/cli.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..31bdbfe8993cdadadbc4896c1e9257b3909b8cc0 --- /dev/null +++ b/setup/cli/cli.inc.php @@ -0,0 +1,31 @@ +<?php +/********************************************************************* + cli.inc.php + + Master include file which must be included at the start of every file. + This is a modification of main.inc.php to support running cli scripts. + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +#Disable direct access. +if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__))) die('kwaheri rafiki!'); + +define('ROOT_PATH', '/'); +define('INC_DIR',dirname(__file__).'/../inc/'); //local include dir! + +require_once(dirname(__file__).'/../../bootstrap.php'); + +Bootstrap::loadConfig(); +Bootstrap::defineTables(TABLE_PREFIX); +Bootstrap::loadCode(); +Bootstrap::i18n_prep(); + +?> diff --git a/setup/cli/manage.php b/setup/cli/manage.php index bfd59f9f0fe2e1b85fcf3cbbbac90744ea4b2290..83d715ac12f87de14aee5aa66bc80140c9bb44f6 100755 --- a/setup/cli/manage.php +++ b/setup/cli/manage.php @@ -46,8 +46,7 @@ class Manager extends Module { if ($val == $action) unset($argv[$idx]); - foreach (glob(dirname(__file__).'/modules/*.php') as $script) - include_once $script; + require_once dirname(__file__)."/modules/{$args['action']}.php"; if (($module = Module::getInstance($action))) return $module->_run($args['action']); diff --git a/setup/cli/modules/class.module.php b/setup/cli/modules/class.module.php index 437f87c609eb6b63f6799978ce324f437a456ca5..a1647ce3cac8d2be9a57e29ab0fe60fe1eb82ce5 100644 --- a/setup/cli/modules/class.module.php +++ b/setup/cli/modules/class.module.php @@ -157,8 +157,18 @@ class Module { if ($this->arguments) { echo "\nArguments:\n"; foreach ($this->arguments as $name=>$help) + $extra = ''; + if (is_array($help)) { + if (isset($help['options']) && is_array($help['options'])) { + foreach($help['options'] as $op=>$desc) + $extra .= wordwrap( + "\n $op - $desc", 76, "\n "); + } + $help = $help['help']; + } echo $name . "\n " . wordwrap( - preg_replace('/\s+/', ' ', $help), 76, "\n "); + preg_replace('/\s+/', ' ', $help), 76, "\n ") + .$extra."\n"; } if ($this->epilog) { @@ -198,6 +208,10 @@ class Module { foreach (array_keys($this->arguments) as $idx=>$name) if (!isset($this->_args[$idx])) $this->optionError($name . " is a required argument"); + elseif (is_array($this->arguments[$name]) + && isset($this->arguments[$name]['options']) + && !isset($this->arguments[$name]['options'][$this->_args[$idx]])) + $this->optionError($name . " does not support such a value"); else $this->_args[$name] = &$this->_args[$idx]; @@ -227,6 +241,11 @@ class Module { function run($args, $options) { } + function fail($message) { + $this->stderr->write($message . "\n"); + die(); + } + /* static */ function register($action, $class) { global $registered_modules; diff --git a/setup/cli/modules/file.php b/setup/cli/modules/file.php new file mode 100644 index 0000000000000000000000000000000000000000..3c9f434b745aee1e50dd3764136510cbfa4e1e47 --- /dev/null +++ b/setup/cli/modules/file.php @@ -0,0 +1,200 @@ +<?php +require_once dirname(__file__) . "/class.module.php"; +require_once dirname(__file__) . "/../cli.inc.php"; + +class FileManager extends Module { + var $prologue = 'CLI file manager for osTicket'; + + var $arguments = array( + 'action' => array( + 'help' => 'Action to be performed', + 'options' => array( + 'list' => 'List files matching criteria', + 'export' => 'Export files from the system', + 'dump' => 'Dump file content to stdout', + 'migrate' => 'Migrate a file to another backend', + 'backends' => 'List configured storage backends', + ), + ), + ); + + var $options = array( + 'ticket' => array('-T', '--ticket', 'metavar'=>'id', + 'help' => 'Search by internal ticket id'), + 'file' => array('-F', '--file', 'metavar'=>'id', + 'help' => 'Search by file id'), + 'name' => array('-N', '--name', 'metavar'=>'name', + 'help' => 'Search by file name (subsring match)'), + 'backend' => array('-b', '--backend', 'metavar'=>'BK', + 'help' => 'Search by file backend. See `backends` action + for a list of available backends'), + 'status' => array('-S', '--status', 'metavar'=>'STATUS', + 'help' => 'Search on ticket state (`open` or `closed`)'), + 'min-size' => array('-z', '--min-size', 'metavar'=>'SIZE', + 'help' => 'Search for files larger than this. k, M, G are welcome'), + 'max-size' => array('-Z', '--max-size', 'metavar'=>'SIZE', + 'help' => 'Search for files smaller than this. k, M, G are welcome'), + + 'limit' => array('-L', '--limit', 'metavar'=>'count', + 'help' => 'Limit search results to this count'), + + 'to' => array('-m', '--to', 'metavar'=>'BK', + 'help' => 'Target backend for migration. See `backends` action + for a list of available backends'), + + 'verbose' => array('-v', '--verbose', 'action'=>'store_true', + 'help' => 'Be more verbose'), + ); + + + function run($args, $options) { + Bootstrap::connect(); + osTicket::start(); + + switch ($args['action']) { + case 'backends': + // List configured backends + foreach (FileStorageBackend::allRegistered() as $char=>$bk) { + print "$char -- {$bk::$desc} ($bk)\n"; + } + break; + + case 'list': + // List files matching criteria + // ORM would be nice! + $files = FileModel::objects(); + $this->_applyCriteria($options, $files); + foreach ($files as $f) { + printf("% 5d %s % 8d %s % 12s %s\n", $f->id, $f->bk, + $f->size, $f->created, $f->type, $f->name); + } + break; + + case 'dump': + $files = FileModel::objects(); + $this->_applyCriteria($options, $files); + if ($files->count() != 1) + $this->fail('Criteria must select exactly 1 file'); + + $f = AttachmentFile::lookup($files[0]->id); + $f->sendData(); + break; + + case 'migrate': + if (!$options['to']) + $this->fail('Please specify a target backend for migration'); + + if (!FileStorageBackend::isRegistered($options['to'])) + $this->fail('Target backend is not installed. See `backends` action'); + + $files = FileModel::objects(); + $this->_applyCriteria($options, $files); + + $count = 0; + foreach ($files as $m) { + $f = AttachmentFile::lookup($m->id); + if ($f->getBackend() == $options['to']) + continue; + if ($options['verbose']) + $this->stdout->write('Migrating '.$m->name."\n"); + try { + if (!$f->migrate($options['to'])) + $this->stderr->write('Unable to migrate '.$m->name."\n"); + else + $count++; + } + catch (IOException $e) { + $this->stderr->write('IOError: '.$e->getMessage()); + } + } + $this->stdout->write("Migrated $count files\n"); + break; + } + + + } + + function _applyCriteria($options, $qs) { + foreach ($options as $name=>$val) { + if (!$val) continue; + switch ($name) { + case 'ticket': + $qs->filter(array('tickets__ticket_id'=>$val)); + break; + case 'file': + $qs->filter(array('id'=>$val)); + break; + case 'name': + $qs->filter(array('name__contains'=>$val)); + break; + case 'backend': + $qs->filter(array('bk'=>$val)); + break; + case 'status': + if (!in_array($val, array('open','closed'))) + $this->fail($val.': Unknown ticket status'); + + $qs->filter(array('tickets__ticket__status'=>$val)); + break; + + case 'min-size': + case 'max-size': + $info = array(); + if (!preg_match('/([\d.]+)([kmgbi]+)?/i', $val, $info)) + $this->fail($val.': Invalid file size'); + if ($info[2]) { + $info[2] = str_replace(array('b','i'), array('',''), $info[2]); + $sizes = array('k'=>1<<10,'m'=>1<<20,'g'=>1<<30); + $val = (float) $val * $sizes[strtolower($info[2])]; + } + if ($name == 'min-size') + $qs->filter(array('size__gte'=>$val)); + else + $qs->filter(array('size__lte'=>$val)); + break; + + case 'limit': + if (!is_numeric($val)) + $this->fail('Provide an result count number to --limit'); + $qs->limit($val); + break; + } + } + } +} + +require_once INCLUDE_DIR . 'class.orm.php'; + +class FileModel extends VerySimpleModel { + static $meta = array( + 'table' => FILE_TABLE, + 'pk' => 'id', + 'joins' => array( + 'tickets' => array( + 'null' => true, + 'constraint' => array('id' => 'TicketAttachmentModel.file_id') + ), + ), + ); +} +class TicketAttachmentModel extends VerySimpleModel { + static $meta = array( + 'table' => TICKET_ATTACHMENT_TABLE, + 'pk' => 'attach_id', + 'joins' => array( + 'ticket' => array( + 'null' => false, + 'constraint' => array('ticket_id' => 'TicketModel.ticket_id'), + ), + ), + ); +} +class TicketModel extends VerySimpleModel { + static $meta = array( + 'table' => TICKET_TABLE, + 'pk' => 'ticket_id', + ); +} + +Module::register('file', 'FileManager'); +?> diff --git a/setup/cli/modules/i18n.php b/setup/cli/modules/i18n.php new file mode 100644 index 0000000000000000000000000000000000000000..a9a4117c4b15f59d6914dbd11edf741b1c5544b7 --- /dev/null +++ b/setup/cli/modules/i18n.php @@ -0,0 +1,130 @@ +<?php + +require_once dirname(__file__) . "/class.module.php"; +require_once dirname(__file__) . "/../cli.inc.php"; +require_once INCLUDE_DIR . 'class.format.php'; + +class i18n_Compiler extends Module { + + var $prologue = "Manages translation files from Crowdin"; + + var $arguments = array( + "command" => "Action to be performed. + list - Show list of available translations" + ); + + var $options = array( + "key" => array('-k','--key','metavar'=>'API-KEY', + 'help'=>'Crowdin project API key. This can be omitted if + CROWDIN_API_KEY is defined in the ost-config.php file'), + "lang" => array('-L', '--lang', 'metavar'=>'code', + 'help'=>'Language code (used for building)'), + ); + + static $crowdin_api_url = 'http://i18n.osticket.com/api/project/osticket-official/{command}'; + + function _http_get($url) { + #curl post + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_USERAGENT, 'osTicket/'.THIS_VERSION); + curl_setopt($ch, CURLOPT_HEADER, FALSE); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + $result=curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + return array($code, $result); + } + + function _request($command, $args=array()) { + + $url = str_replace('{command}', $command, self::$crowdin_api_url); + + $args += array('key' => $this->key); + foreach ($args as &$a) + $a = urlencode($a); + unset($a); + $url .= '?' . Format::array_implode('=', '&', $args); + + return $this->_http_get($url); + } + + function run($args, $options) { + $this->key = $options['key']; + if (!$this->key && defined('CROWDIN_API_KEY')) + $this->key = CROWDIN_API_KEY; + + switch (strtolower($args['command'])) { + case 'list': + if (!$this->key) + $this->fail('API key is required'); + $this->_list(); + break; + case 'build': + if (!$this->key) + $this->fail('API key is required'); + if (!$options['lang']) + $this->fail('Language code is required. See `list`'); + $this->_build($options['lang']); + break; + } + } + + function _list() { + error_reporting(E_ALL); + list($code, $body) = $this->_request('status'); + $d = new DOMDocument(); + $d->loadXML($body); + + $xp = new DOMXpath($d); + foreach ($xp->query('//language') as $c) { + $name = $code = ''; + foreach ($c->childNodes as $n) { + switch (strtolower($n->nodeName)) { + case 'name': + $name = $n->textContent; + break; + case 'code': + $code = $n->textContent; + break; + } + } + if (!$code) + continue; + $this->stdout->write(sprintf("%s (%s)\n", $code, $name)); + } + } + + function _build($lang) { + list($code, $zip) = $this->_request("download/$lang.zip"); + + if ($code !== 200) + $this->fail('Language is not available'."\n"); + + $temp = tempnam('/tmp', 'osticket-cli'); + $f = fopen($temp, 'w'); + fwrite($f, $zip); + fclose($f); + $zip = new ZipArchive(); + $zip->open($temp); + unlink($temp); + + $lang = str_replace('-','_',$lang); + @unlink(I18N_DIR."$lang.phar"); + $phar = new Phar(I18N_DIR."$lang.phar"); + + for ($i=0; $i<$zip->numFiles; $i++) { + $info = $zip->statIndex($i); + $phar->addFromString($info['name'], $zip->getFromIndex($i)); + } + + // TODO: Add i18n extras (like fonts) + + // TODO: Sign files + } +} + +Module::register('i18n', 'i18n_Compiler'); +?> diff --git a/setup/cli/package.php b/setup/cli/package.php index e5b4eb9b7ebc93c1e3206d683d29a287185d6a6f..10fa537644dde35388e29a488aa19961dbdcf68e 100755 --- a/setup/cli/package.php +++ b/setup/cli/package.php @@ -115,7 +115,7 @@ mkdir("$stage_path/scripts/"); package("setup/scripts/*", "scripts/", -1, "*stage"); # Load the heart of the system -package("include/{,.}*", "upload/include", -1, array('*ost-config.php', '*.sw[a-z]')); +package("include/{,.}*", "upload/include", -1, array('*ost-config.php', '*.sw[a-z]','plugins/*')); # Include the installer package("setup/*.{php,txt,html}", "upload/setup", -1, array("*scripts","*test","*stage")); diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index d08fc34ed0f5a4b9890baf07fb0b6012e965a059..cc18ba3e949ced04453f15205329a297b4315b84 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -15,6 +15,7 @@ **********************************************************************/ require_once INCLUDE_DIR.'class.migrater.php'; require_once INCLUDE_DIR.'class.setup.php'; +require_once INCLUDE_DIR.'class.i18n.php'; class Installer extends SetupWizard { @@ -148,10 +149,9 @@ class Installer extends SetupWizard { } if(!$this->errors) { - // TODO: Use language selected from install worksheet - require_once INCLUDE_DIR.'class.i18n.php'; - $i18n = new Internationalization('en_US'); + // TODO: Use language selected from install worksheet + $i18n = new Internationalization($vars['lang_id']); $i18n->loadDefaultData(); $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1'; diff --git a/setup/inc/install.inc.php b/setup/inc/install.inc.php index e2d6a5a0f3b87b28f7466a91780b2d5d932956fb..f58943a2fc450022077fd2d1ba59f752b636c66f 100644 --- a/setup/inc/install.inc.php +++ b/setup/inc/install.inc.php @@ -1,8 +1,8 @@ -<?php +<?php if(!defined('SETUPINC')) die('Kwaheri!'); -$info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbhost'=>'localhost'); +$info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbhost'=>'localhost','lang_id'=>'en_US'); ?> -<div id="main" class="step2"> +<div id="main" class="step2"> <h1>osTicket Basic Installation</h1> <p>Please fill out the information below to continue your osTicket installation. All fields are required.</p> <font class="error"><strong><?php echo $errors['err']; ?></strong></font> @@ -26,6 +26,19 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbho <a class="tip" href="#system_email"><i class="icon-question-sign help-tip"></i></a> <font class="error"><?php echo $errors['email']; ?></font> </div> + <div class="row"> + <label>Default Language:</label> +<?php $langs = Internationalization::availableLanguages(); ?> + <select name="lang_id"> +<?php foreach($langs as $l) { + $selected = ($info['lang_id'] == $l['code']) ? 'selected="selected"' : ''; ?> + <option value="<?php echo $l['code']; ?>" <?php echo $selected; + ?>><?php echo $l['desc']; ?></option> +<?php } ?> + </select> + <a class="tip" href="#default_lang"><i class="icon-question-sign help-tip"></i></a> + <font class="error"> <?php echo $errors['lang_id']; ?></font> + </div> <h4 class="head admin">Admin User</h4> <span class="subhead">Your primary administrator account - you can add more users later.</span> diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index 8ab5676efd090558868eef5065c14b19882af0ed..85f06999698f21093930575429e967ff7e28ab29 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -33,8 +33,8 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%faq` ( `answer` text NOT NULL, `keywords` tinytext, `notes` text, - `created` date NOT NULL, - `updated` date NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, PRIMARY KEY (`faq_id`), UNIQUE KEY `question` (`question`), KEY `category_id` (`category_id`), @@ -276,7 +276,7 @@ CREATE TABLE `%TABLE_PREFIX%filter_rule` ( `id` int(11) unsigned NOT NULL auto_increment, `filter_id` int(10) unsigned NOT NULL default '0', `what` varchar(32) NOT NULL, - `how` enum('equal','not_equal','contains','dn_contain','starts','ends') NOT NULL, + `how` enum('equal','not_equal','contains','dn_contain','starts','ends','match','not_match') NOT NULL, `val` varchar(255) NOT NULL, `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1', `notes` tinytext NOT NULL, @@ -317,14 +317,19 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%file`; CREATE TABLE `%TABLE_PREFIX%file` ( `id` int(11) NOT NULL auto_increment, `ft` CHAR( 1 ) NOT NULL DEFAULT 'T', - `type` varchar(255) NOT NULL default '', - `size` varchar(25) NOT NULL default '', - `hash` varchar(125) NOT NULL, + `bk` CHAR( 1 ) NOT NULL DEFAULT 'D', + -- RFC 4288, Section 4.2 declares max MIMEType at 255 ascii chars + `type` varchar(255) collate ascii_general_ci NOT NULL default '', + `size` bigint(20) unsigned NOT NULL default 0, + `key` varchar(86) collate ascii_general_ci NOT NULL, + `signature` varchar(86) collate ascii_bin NOT NULL, `name` varchar(255) NOT NULL default '', + `attrs` varchar(255), `created` datetime NOT NULL, PRIMARY KEY (`id`), KEY `ft` (`ft`), - KEY `hash` (`hash`) + KEY `key` (`key`), + KEY `signature` (`signature`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%file_chunk`; @@ -435,6 +440,7 @@ CREATE TABLE `%TABLE_PREFIX%staff` ( `firstname` varchar(32) default NULL, `lastname` varchar(32) default NULL, `passwd` varchar(128) default NULL, + `backend` varchar(32) default NULL, `email` varchar(128) default NULL, `phone` varchar(24) NOT NULL default '', `phone_ext` varchar(6) default NULL, @@ -505,7 +511,7 @@ CREATE TABLE `%TABLE_PREFIX%team_member` ( DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket`; CREATE TABLE `%TABLE_PREFIX%ticket` ( `ticket_id` int(11) unsigned NOT NULL auto_increment, - `ticketID` int(11) unsigned NOT NULL default '0', + `number` varchar(20), `user_id` int(11) unsigned NOT NULL default '0', `user_email_id` int(11) unsigned NOT NULL default '0', `dept_id` int(10) unsigned NOT NULL default '0', @@ -513,6 +519,7 @@ CREATE TABLE `%TABLE_PREFIX%ticket` ( `topic_id` int(10) unsigned NOT NULL default '0', `staff_id` int(10) unsigned NOT NULL default '0', `team_id` int(10) unsigned NOT NULL default '0', + `email_id` int(11) unsigned NOT NULL default '0', `ip_address` varchar(64) NOT NULL default '', `status` enum('open','closed') NOT NULL default 'open', `source` enum('Web','Email','Phone','API','Other') NOT NULL default 'Other', @@ -543,11 +550,10 @@ CREATE TABLE `%TABLE_PREFIX%ticket_attachment` ( `ticket_id` int(11) unsigned NOT NULL default '0', `file_id` int(10) unsigned NOT NULL default '0', `ref_id` int(11) unsigned NOT NULL default '0', - `ref_type` enum('M','R','N') NOT NULL default 'M', + `inline` tinyint(1) NOT NULL default '0', `created` datetime NOT NULL, PRIMARY KEY (`attach_id`), KEY `ticket_id` (`ticket_id`), - KEY `ref_type` (`ref_type`), KEY `ref_id` (`ref_id`), KEY `file_id` (`file_id`) ) DEFAULT CHARSET=utf8; @@ -566,10 +572,12 @@ CREATE TABLE `%TABLE_PREFIX%ticket_lock` ( DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_email_info`; CREATE TABLE `%TABLE_PREFIX%ticket_email_info` ( - `message_id` int(11) unsigned NOT NULL, + `id` int(11) unsigned NOT NULL auto_increment, + `thread_id` int(11) unsigned NOT NULL, `email_mid` varchar(255) NOT NULL, `headers` text, - KEY `message_id` (`email_mid`) + PRIMARY KEY (`id`), + KEY `email_mid` (`email_mid`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_event`; @@ -607,6 +615,7 @@ CREATE TABLE `%TABLE_PREFIX%ticket_thread` ( `pid` int(11) unsigned NOT NULL default '0', `ticket_id` int(11) unsigned NOT NULL default '0', `staff_id` int(11) unsigned NOT NULL default '0', + `user_id` int(11) unsigned not null default 0, `thread_type` enum('M','R','N') NOT NULL, `poster` varchar(128) NOT NULL default '', `source` varchar(32) NOT NULL default '', @@ -621,6 +630,21 @@ CREATE TABLE `%TABLE_PREFIX%ticket_thread` ( KEY `pid` (`pid`) ) DEFAULT CHARSET=utf8; +CREATE TABLE `%TABLE_PREFIX%ticket_collaborator` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `isactive` tinyint(1) NOT NULL DEFAULT '1', + `ticket_id` int(11) unsigned NOT NULL DEFAULT '0', + `user_id` int(11) unsigned NOT NULL DEFAULT '0', + -- M => (message) clients, N => (note) 3rd-Party, R => (reply) external authority + `role` char(1) NOT NULL DEFAULT 'M', + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `collab` (`ticket_id`,`user_id`) +) DEFAULT CHARSET=utf8; + + + DROP TABLE IF EXISTS `%TABLE_PREFIX%timezone`; CREATE TABLE `%TABLE_PREFIX%timezone` ( `id` int(11) unsigned NOT NULL auto_increment, @@ -676,6 +700,18 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%page` ( UNIQUE KEY `name` (`name`) ) DEFAULT CHARSET=utf8; +-- Plugins +DROP TABLE IF EXISTS `%TABLE_PREFIX%plugin`; +CREATE TABLE `%TABLE_PREFIX%plugin` ( + `id` int(11) unsigned not null auto_increment, + `name` varchar(30) not null, + `install_path` varchar(60) not null, + `isphar` tinyint(1) not null default 0, + `isactive` tinyint(1) not null default 0, + `installed` datetime not null, + primary key (`id`) +) DEFAULT CHARSET=utf8; + DROP TABLE IF EXISTS `%TABLE_PREFIX%user`; CREATE TABLE `%TABLE_PREFIX%user` ( `id` int(10) unsigned NOT NULL auto_increment, diff --git a/setup/test/tests/class.php_analyze.php b/setup/test/tests/class.php_analyze.php index 153c9ad11fa99ac7a731a8b7492c6cbc4b9c6b92..53e0a85ab342851a322b102ecde9899a6361cee0 100644 --- a/setup/test/tests/class.php_analyze.php +++ b/setup/test/tests/class.php_analyze.php @@ -73,13 +73,19 @@ class SourceAnalyzer extends Test { $scope = array(); while ($token != "{") { list(,$token) = each($this->tokens); - if (!is_array($token) - || $token[0] == T_WHITESPACE) + switch ($token[0]) { + case T_WHITESPACE: continue; - if ($token[0] == T_STRING) + case T_STRING: $function['name'] = $token[1]; - elseif ($token[0] == T_VARIABLE) + break; + case T_VARIABLE: $scope[$token[1]] = 1; + break; + case ';': + // Abstract function -- no body will follow + return; + } } // Start inside a block -- we've already consumed the { $this->checkVariableUsage($function, $scope, 1, $options); diff --git a/setup/test/tests/mockdb.php b/setup/test/tests/mockdb.php new file mode 100644 index 0000000000000000000000000000000000000000..b6bc348cf0ddf50be84cd9fe24e4237c74f9ec90 --- /dev/null +++ b/setup/test/tests/mockdb.php @@ -0,0 +1,82 @@ +<?php + +function db_connect($source) { + global $__db; + $__db = $source; +} + +function db_input($what) { + return sprintf("'%8.8s'", md5($what)); +} + +function db_query($sql) { + global $__db; + + return $__db->query($sql); +} + +function db_fetch_row($res) { + return $res->fetch_row(); +} + +function db_fetch_array($res) { + return $res->fetch_array(); +} + +function db_affected_row() { + global $__db; + return $__db->affected_rows; +} +function db_insert_id() { + global $__db; + return $__db->insert_id; +} + +function db_num_rows($res) { + return $res->num_rows(); +} + +class MockDbSource { + var $insert_id = 1; + var $affected_rows = 1; + + var $data; + + function __construct($data=array()) { + $this->data = $data; + } + + function query($sql) { + $hash = md5($sql); + if (!isset($this->data[$sql])) + print ($hash.": No data found:\n".$sql."\n"); + + return new MockDbCursor($this->data[$hash] ?: array()); + } + + function addRecordset($hash, &$data) { + $this->data[$hash] = $data; + } +} + +class MockDbCursor { + var $data; + + function __construct($data) { + $this->data = $data; + } + + function fetch_row() { + list($i, $row) = each($this->data); + return $row; + } + + function fetch_array() { + list($i, $row) = each($this->data); + return $row; + } + + function num_rows() { + return count($this->data); + } +} diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php index 1f82fa1be9cb989a60c64d3b13a2d5707afd264f..1a40562c49f4fb02f8ad8a02214f617fe0c350fc 100644 --- a/setup/test/tests/stubs.php +++ b/setup/test/tests/stubs.php @@ -46,6 +46,7 @@ class DomElement { class DomDocument { function loadHTML() {} + function loadXML() {} } class Exception { @@ -84,6 +85,15 @@ class DateTimeZone { static function listIdentifiers() {} } +class Phar { + static function isValidPharFilename() {} +} + +class ZipArchive { + function statIndex() {} + function getFromIndex() {} +} + class finfo { function file() {} function buffer() {} diff --git a/setup/test/tests/test.mail-parse.php b/setup/test/tests/test.mail-parse.php new file mode 100644 index 0000000000000000000000000000000000000000..d1d80c225bc2ac846e62aa17b71d2a2f93128130 --- /dev/null +++ b/setup/test/tests/test.mail-parse.php @@ -0,0 +1,53 @@ +<?php + +require_once INCLUDE_DIR.'class.validator.php'; +require_once INCLUDE_DIR.'class.auth.php'; +require_once INCLUDE_DIR.'class.staff.php'; +require_once INCLUDE_DIR.'class.email.php'; +require_once INCLUDE_DIR.'class.format.php'; +require_once INCLUDE_DIR.'class.thread.php'; + +require_once 'mockdb.php'; + +class TestMailParsing extends Test { + var $name = "Mail parsing library tests"; + + function testRecipients() { + db_connect(new MockDbSource()); + $email = <<<EOF +Delivered-To: jared@osticket.com +Received: by 10.60.55.168 with SMTP id t8csp161432oep; + Fri, 7 Feb 2014 22:11:19 -0800 (PST) +X-Received: by 10.182.18.9 with SMTP id s9mr16356699obd.15.1391839879167; + Fri, 07 Feb 2014 22:11:19 -0800 (PST) +Return-Path: <mailer@greezybacon.supportsystem.com> +To: jared@osticket.com +Subject: =?utf-8?Q?System_test_email_=C2=AE?= +Content-Type: multipart/alternative; + boundary="=_28022448a1f58a3af7edf57ff2e3af44" +From: "Support" <help@supportsystem.com> +Date: Sat, 08 Feb 2014 01:11:18 -0500 +Message-ID: <Syke6-g24hwuTu77-help@supportsystem.com> +MIME-Version: 1.0 + +--=_28022448a1f58a3af7edf57ff2e3af44 +Content-Transfer-Encoding: base64 +Content-Type: text/plain; charset=utf-8 + +Q2hlZXJzISE= +--=_28022448a1f58a3af7edf57ff2e3af44 +Content-Transfer-Encoding: base64 +Content-Type: text/html; charset=utf-8 + +Q2hlZXJzISE= +--=_28022448a1f58a3af7edf57ff2e3af44-- +EOF; + + $result = EmailDataParser::parse($email); + $this->assert(count($result['recipients']) == 1, 'Expected 1 recipient'); + $this->assert($result['recipients'][0]['source'] == 'delivered-to', + 'Delivered-To header used as a collaborator'); + } +} +return 'TestMailParsing'; +?> diff --git a/tickets.php b/tickets.php index b47ed0f1528335e9ee5eb871fcd8ca3c34d7b975..01d8b799c4220aa9ffc66ad7ce3e238ef2984925 100644 --- a/tickets.php +++ b/tickets.php @@ -20,10 +20,10 @@ require_once(INCLUDE_DIR.'class.ticket.php'); require_once(INCLUDE_DIR.'class.json.php'); $ticket=null; if($_REQUEST['id']) { - if(!($ticket=Ticket::lookupByExtId($_REQUEST['id']))) { + if (!($ticket = Ticket::lookup($_REQUEST['id']))) { $errors['err']='Unknown or invalid ticket ID.'; - }elseif(!$ticket->checkClientAccess($thisclient)) { - $errors['err']='Unknown or invalid ticket ID.'; //Using generic message on purpose! + } elseif(!$ticket->checkUserAccess($thisclient)) { + $errors['err']='Unknown or invalid ticket.'; //Using generic message on purpose! $ticket=null; } } @@ -32,8 +32,27 @@ if($_REQUEST['id']) { if($_POST && is_object($ticket) && $ticket->getId()): $errors=array(); switch(strtolower($_POST['a'])){ + case 'edit': + if(!$ticket->checkUserAccess($thisclient)) //double check perm again! + $errors['err']='Access Denied. Possibly invalid ticket ID'; + elseif (!$cfg || !$cfg->allowClientUpdates()) + $errors['err']='Access Denied. Client updates are currently disabled'; + else { + $forms=DynamicFormEntry::forTicket($ticket->getId()); + foreach ($forms as $form) + if (!$form->isValid()) + $errors = array_merge($errors, $form->errors()); + } + if (!$errors) { + foreach ($forms as $f) $f->save(); + $_REQUEST['a'] = null; //Clear edit action - going back to view. + $ticket->logNote('Ticket details updated', sprintf( + 'Ticket details were updated by client %s <%s>', + $thisclient->getName(), $thisclient->getEmail())); + } + break; case 'reply': - if(!$ticket->checkClientAccess($thisclient)) //double check perm again! + if(!$ticket->checkUserAccess($thisclient)) //double check perm again! $errors['err']='Access Denied. Possibly invalid ticket ID'; if(!$_POST['message']) @@ -41,7 +60,10 @@ if($_POST && is_object($ticket) && $ticket->getId()): if(!$errors) { //Everything checked out...do the magic. - $vars = array('message'=>$_POST['message']); + $vars = array( + 'userId' => $thisclient->getId(), + 'poster' => (string) $thisclient->getName(), + 'message' => $_POST['message']); if($cfg->allowOnlineAttachments() && $_FILES['attachments']) $vars['files'] = AttachmentFile::format($_FILES['attachments'], true); if (isset($_POST['draft_id'])) @@ -51,7 +73,7 @@ if($_POST && is_object($ticket) && $ticket->getId()): $msg='Message Posted Successfully'; // Cleanup drafts for the ticket. If not closed, only clean // for this staff. Else clean all drafts for the ticket. - Draft::deleteForNamespace('ticket.client.' . $ticket->getExtId()); + Draft::deleteForNamespace('ticket.client.' . $ticket->getId()); } else { $errors['err']='Unable to post the message. Try again'; } @@ -66,8 +88,16 @@ if($_POST && is_object($ticket) && $ticket->getId()): $ticket->reload(); endif; $nav->setActiveNav('tickets'); -if($ticket && $ticket->checkClientAccess($thisclient)) { - $inc='view.inc.php'; +if($ticket && $ticket->checkUserAccess($thisclient)) { + if (isset($_REQUEST['a']) && $_REQUEST['a'] == 'edit' + && $cfg->allowClientUpdates()) { + $inc = 'edit.inc.php'; + if (!$forms) $forms=DynamicFormEntry::forTicket($ticket->getId()); + // Auto add new fields to the entries + foreach ($forms as $f) $f->addMissingFields(); + } + else + $inc='view.inc.php'; } elseif($cfg->showRelatedTickets() && $thisclient->getNumTickets()) { $inc='tickets.inc.php'; } else { diff --git a/view.php b/view.php index 10e5374fe71b8dcf3551c0ee922ac5b4800e961a..b8590aab4f5ddaa25f004123dbfcbae950965a2a 100644 --- a/view.php +++ b/view.php @@ -3,7 +3,6 @@ view.php Ticket View. - TODO: Support different views based on auth_token - e.g for BCC'ed users vs. Ticket owner. Peter Rotich <peter@osticket.com> Copyright (c) 2006-2010 osTicket @@ -17,19 +16,12 @@ **********************************************************************/ require_once('client.inc.php'); -//If the user is NOT logged in - try auto-login (if params exists). -if(!$thisclient || !$thisclient->isValid()) { - // * On login Client::login will redirect the user to tickets.php view. - // * See TODO above for planned multi-view. - $user = null; - if($_GET['t'] && $_GET['e'] && $_GET['a']) - $user = Client::login($_GET['t'], $_GET['e'], $_GET['a'], $errors); - - //XXX: For now we're assuming the user is the ticket owner - // (multi-view based on auth token will come later). - if($user && $user->getTicketID()==trim($_GET['t'])) - @header('Location: tickets.php?id='.$user->getTicketID()); -} +// Try autologin the user +// Authenticated user can be of type ticket owner or collaborator +$errors = array(); +$user = UserAuthenticationBackend::processSignOn($errors); +if ($user && $user->getTicketId()) + Http::redirect('tickets.php?id='.$user->getTicketId()); //Simply redirecting to tickets.php until multiview is implemented. require('tickets.php');