<?php
/*
USPS Rate V4 Intl Rate V2 - v.1.7
  $Mod: check for curl (avoids transfer-encoding:chunked issue), use this if available, otherwise old http_client method 20131116 codemehappy.com $
  $Mod: Changed from Parcel Post to Standard Post 20130129 Kymation $
  $Mod: USPS API changes 20130729 Kymation v 1.3 $
Copyright (c) 2012 osCbyJetta
This is the new module working for all methods.@devidash Loaded Commerce 2018
Released under the GNU General Public License
*/

  class usps {
    var $code, $title, $description, $icon, $enabled, $usps_weight;

    function __construct() {
      global $order;

      $this->code = 'usps';
      $this->title = lc_defined_constant('MODULE_SHIPPING_USPS_TEXT_TITLE');
      $this->description = lc_defined_constant('MODULE_SHIPPING_USPS_TEXT_DESCRIPTION');
      $this->sort_order = lc_defined_constant('MODULE_SHIPPING_USPS_SORT_ORDER');
      $this->icon = DIR_WS_ICONS . 'shipping_usps.gif';
      $this->tax_class = lc_defined_constant('MODULE_SHIPPING_USPS_TAX_CLASS');
      $this->enabled = (lc_check_constant_val('MODULE_SHIPPING_USPS_STATUS', 'True') ? true : false);
      $usps_zone = lc_defined_constant_int('MODULE_SHIPPING_USPS_ZONE');
      if ($this->enabled == true && $usps_zone > 0) {
        $check_flag = false;
        $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_SHIPPING_USPS_ZONE . "' and zone_country_id = '" . $order->delivery['country']['id'] . "' order by zone_id");
        while ($check = tep_db_fetch_array($check_query)) {
          if ($check['zone_id'] < 1)
            $check_flag = true;
          elseif ($check['zone_id'] == $order->delivery['zone_id']) $check_flag = true;
        }
        if ($check_flag == false)
          $this->enabled = false;
      }
    }
    
    function quote($method = '') {
      global $order, $shipping_num_boxes, $currencies, $shipping, $shipping_weight;
      
      $iInfo = '';
      $location = '';
      $methods = array ();

      $this->usps_weight = ( $shipping_weight < 0.0625 ? 0.0625 : $shipping_weight );
      $this->pounds = (int) $this->usps_weight;
      $this->ounces = ceil(round(16 * ($this->usps_weight - $this->pounds), 3));
      
      //Get the quote from USPS
      $uspsQuote = $this->_getQuote();
      //print_r($uspsQuote);
      //exit();
      
      if (isset ($uspsQuote['Number']))
        return false;
      if ($order->delivery['country']['iso_code_2'] == 'US') {
        $dExtras = array ();
        $dOptions = explode(', ', MODULE_SHIPPING_USPS_DMST_SERVICES);
        foreach ($dOptions as $key => $val) {
          if (strlen($dOptions[$key]) > 1) {
            if ($dOptions[$key +1] == 'C' || $dOptions[$key +1] == 'S' || $dOptions[$key +1] == 'H') {
              $dExtras[$dOptions[$key]] = $dOptions[$key +1];
            }
          }
        }
      } else {
        $iExtras = array ();
        $iOptions = explode(', ', MODULE_SHIPPING_USPS_INTL_SERVICES);
        foreach ($iOptions as $key => $val) {
          if (strlen($iOptions[$key]) > 1) {
            if ($iOptions[$key +1] == 'C' || $iOptions[$key +1] == 'S' || $iOptions[$key +1] == 'H') {
              $iExtras[$iOptions[$key]] = $iOptions[$key +1];
            }
          }
        }
        if (MODULE_SHIPPING_USPS_REGULATIONS == 'True') {
          $location = tep_get_country_name($order->delivery['country']['id']);
          $intreg = '<span>' . sprintf( MODULE_SHIPPING_USPS_TEXT_INTL_SHOW, $location) . '</span>';
          $intreghide = '<span>' . sprintf( MODULE_SHIPPING_USPS_TEXT_INTL_HIDE, $location) . '</span>';

          $iInfo = '<div id="iInfo">' .
            '<div id="showInfo" class="ui-state-error" style="cursor:pointer; text-align:center;" onclick="$(\'#showInfo\').hide();$(\'#hideInfo, #Info\').show();">' . $intreg . '</div>' .
            '<div id="hideInfo" class="ui-state-error" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#hideInfo, #Info\').hide();$(\'#showInfo\').show();">' . $intreghide . '</div>' .
            '<div id="Info" class="ui-state-highlight" style="display:none; padding:10px; max-height:200px; overflow:auto;">' . '<b>Prohibitions:</b><br>' . nl2br($uspsQuote['Package']['Prohibitions']) . '<br><br><b>Restrictions:</b><br>' . nl2br($uspsQuote['Package']['Restrictions']) . '<br><br><b>Observations:</b><br>' . nl2br($uspsQuote['Package']['Observations']) . '<br><br><b>CustomsForms:</b><br>' . nl2br($uspsQuote['Package']['CustomsForms']) . '<br><br><b>PriorityMailExpress:</b><br>' . nl2br($uspsQuote['Package']['PriorityMailExpress']) . '<br><br><b>AreasServed:</b><br>' . nl2br($uspsQuote['Package']['AreasServed']) . '<br><br><b>AdditionalRestrictions:</b><br>' . nl2br($uspsQuote['Package']['AdditionalRestrictions']) . '</div>' .
            '</div>';
        }
      }
      
      if (isset ($uspsQuote['Package']['Postage']) && tep_not_null($uspsQuote['Package']['Postage'])) {
        $PackageSize = 1;
      } else {
        $PackageSize = ($order->delivery['country']['iso_code_2'] == 'US' ? sizeof($uspsQuote['Package']) : sizeof($uspsQuote['Package']['Service']));
      }
        
      for ($i = 0; $i < $PackageSize; $i++) {
        $Services = array ();
        $shownServices = array ();
        $hiddenServices = array ();
        $customerServices = array ();
        $hiddenCost = 0;
        $shownCost = 0;
        $shownString = '';
        $customerString = '';
        $handling = 0;
        $types = explode(', ', MODULE_SHIPPING_USPS_TYPES);
        if (isset ($uspsQuote['Package'][$i]['Error']) && tep_not_null($uspsQuote['Package'][$i]['Error']))
          continue;
        $Package = ($PackageSize == 1 ? $uspsQuote['Package']['Postage'] : ($order->delivery['country']['iso_code_2'] == 'US' ? $uspsQuote['Package'][$i]['Postage'] : $uspsQuote['Package']['Service'][$i]));
        if ($order->delivery['country']['iso_code_2'] == 'US') {
          if (tep_not_null($Package['SpecialServices']['SpecialService']))
            foreach ($Package['SpecialServices']['SpecialService'] as $key => $val)
              if (isset ($dExtras[$val['ServiceName']]) && tep_not_null($dExtras[$val['ServiceName']]) && ((MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' && $val['AvailableOnline'] == 'true') || (MODULE_SHIPPING_USPS_RATE_TYPE == 'Retail' && $val['Available'] == 'true'))) {
                $val['ServiceAdmin'] = $dExtras[$val['ServiceName']];
                $Services[] = $val;
              }
          $cost = MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' && tep_not_null($Package['CommercialRate']) ? $Package['CommercialRate'] : $Package['Rate'];
          $type = $Package['MailService'];
        } else {
          foreach ($Package['ExtraServices']['ExtraService'] as $key => $val) {
            if (isset ($iExtras[$val['ServiceName']]) && tep_not_null($iExtras[$val['ServiceName']]) && ((MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' && $val['AvailableOnline'] == 'True') || (MODULE_SHIPPING_USPS_RATE_TYPE == 'Retail' && $val['Available'] == 'True'))) {
              $val['ServiceAdmin'] = $iExtras[$val['ServiceName']];
              $Services[] = $val;
            }
          }
          $cost = MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' && tep_not_null($Package['CommercialPostage']) ? $Package['CommercialPostage'] : $Package['Postage'];
          $type = $Package['SvcDescription'];
        }
        
        if ($cost == 0) {
          continue;
        }
          
        if($type == 'First-Class Package Service - RetailTM')
        	$type = 'First-Class MailRM Parcel';
        if($type == 'USPS Retail GroundTM')
        	$type = 'Standard PostRM';

        foreach ($types as $key => $val) {
          if (!is_numeric($val) && $val == $type) {
            $minweight = $types[$key +1];
            $maxweight = $types[$key +2];
            $handling = $types[$key +3];
          }
        }
        
        foreach ($Services as $key => $val) {
          $sDisplay = $Services[$key]['ServiceAdmin'];
          if ($sDisplay == 'H')
            $hiddenServices[] = array (
              $Services[$key]['ServiceName'] => (MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])
            );
          elseif ($sDisplay == 'S') $shownServices[] = array (
            $Services[$key]['ServiceName'] => (MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])
          );
          elseif ($sDisplay == 'C') $customerServices[] = array (
            $Services[$key]['ServiceName'] => (MODULE_SHIPPING_USPS_RATE_TYPE == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])
          );
        }
        
        foreach ($hiddenServices as $key => $val) {
          foreach ($hiddenServices[$key] as $key1 => $val1) {
            $hiddenCost += $val1;
          }
        }
        
        if (sizeof($shownServices) > 0) {
          $shownString = '<div id="shownString" style="float:right; padding-right:10px; display:none;">' .
          '<div id="shownStringShow" style="cursor:pointer; text-align:center;" onclick="$(\'#shownStringShow\', $(this).parent().parent()).hide();$(\'#shownStringHide, #shownStringInfo\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_SSTRING_SHOW . '</div>' .
          '<div id="shownStringHide" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#shownStringHide, #shownStringInfo\', $(this).parent().parent()).hide();$(\'#shownStringShow\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_SSTRING_HIDE . '</div>' .
          '</div><div style="clear:both;"></div>' .
          '<div id="shownStringInfo" style="display:none;">' .
          '<div style="padding-left:20px; float:left;">' . MODULE_SHIPPING_USPS_TEXT_BASE_COST . '</div><div style="padding-right:20px; float:right;">' . ($cost == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format(($cost + (sizeof($hiddenServices) > 0 ? $handling + $hiddenCost : 0)) * $shipping_num_boxes)) . '</div><div style="clear:both;"></div>';
          if (sizeof($hiddenServices) == 0) {
            $shownString .= '<div style="padding-left:20px; float:left;">' . MODULE_SHIPPING_USPS_TEXT_HANDLING_COST . '</div>' .
              '<div style="padding-right:20px; float:right;">' . ($handling == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($handling * $shipping_num_boxes)) . '</div>' .
              '<div style="clear:both;"></div>' .
              '<div style="clear:both;"></div>';
          }
          foreach ($shownServices as $key => $val) {
            foreach ($shownServices[$key] as $key1 => $val1) {
              $shownString .= '<div style="padding-left:20px; float:left;">' . str_replace(array (
                'RM',
                'TM'
              ), array (
                '&reg;',
                '&trade;'
              ), $key1) . '</div><div style="padding-right:20px; float:right;">' . ($val1 == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($val1 * $shipping_num_boxes)) . '</div><div style="clear:both;"></div>';
              $shownCost = $shownCost + $val1;
            }
          }
          $shownString .= '</div><div style="clear:both;"></div>';
        }
        
        if (sizeof($customerServices) > 0) {
          $customerString = '<div id="customerString" style="float:right; padding-right:10px; display:none;">' .
          '<div id="customerStringShow" style="cursor:pointer; text-align:center;" onclick="$(\'#customerStringShow\', $(this).parent().parent()).hide();$(\'#customerStringHide, #customerStringInfo\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_CSTRING_SHOW . '</div>' .
          '<div id="customerStringHide" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#customerStringHide, #customerStringInfo\', $(this).parent().parent()).hide();$(\'#customerStringShow\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_CSTRING_HIDE . '</div>' .
          '</div><div style="clear:both;"></div>' .
          '<div id="customerStringInfo" style="display:none;">';
          foreach ($customerServices as $key => $val) {
            foreach ($customerServices[$key] as $key1 => $val1) {
              $customerString .= '<div style="padding-left:20px; float:left;">' . str_replace(array (
                'RM',
                'TM'
              ), array (
                '&reg;',
                '&trade;'
              ), $key1) . '</div>' .
              '<div style="padding-right:20px; float:right;"><input type="checkbox" name="' . $key1 . '" value="' . $val1 * $shipping_num_boxes . '" id="' . $type . '"></div>' .
              '<div style="padding-right:5px; float:right;">' . ($val1 == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($val1 * $shipping_num_boxes)) . '</div>' .
              '<div style="clear:both;"></div>';
              $customerCost = $customerCost + $val1;
            }
          }
          $customerString .= '</div><div style="clear:both;"></div>';
        }

        if ((($method == '' && in_array($type, $types)) || $method == $type) && $this->usps_weight < $maxweight && $this->usps_weight > $minweight) {
          $methods[] = array (
            'id' => $type,
            'title' => str_replace(array (
              'RM',
              'TM',
              '**'
            ), array (
              '<sup>&#174;</sup>',
              '<sup>&#8482;</sup>',
              ''
            ), $type),
            'cost' => ($cost + $handling + $hiddenCost + $shownCost) * $shipping_num_boxes,
            'shownString' => (string) $shownString,
            'customerString' => (string) $customerString
          );
        }
      }
      if (sizeof($methods) == 0)
        return false;
      if (sizeof($methods) > 1) {
        foreach ($methods as $c => $key) {
          $sort_cost[] = $key['cost'];
          $sort_id[] = $key['id'];
        }
        array_multisort($sort_cost, (MODULE_SHIPPING_USPS_RATE_SORTER == 'Ascending' ? SORT_ASC : SORT_DESC), $sort_id, SORT_ASC, $methods);
      }

      $weight_text = MODULE_SHIPPING_USPS_WEIGHTS == 'True' ? ' ' . $this->pounds . ' lbs, ' . $this->ounces . ' oz' : '';
      $this->quotes = array (
        'id' => $this->code,
        'module' => $this->title . $weight_text,
        'methods' => $methods,
        'tax' => $this->tax_class > 0 ? tep_get_tax_rate($this->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']) : null,
        'icon' => tep_not_null($this->icon) || tep_not_null($iInfo) ? (tep_not_null($this->icon) ? tep_image($this->icon, $this->title) : '') . (tep_not_null($iInfo) ? '<br>' . $iInfo : '') : null
      );
      
      return $this->quotes;
    }
    
    function _getQuote() {
      global $order;
      
      // Build a request string
      if ($order->delivery['country']['iso_code_2'] == 'US') {
        $ZipDestination = substr(str_replace(' ', '', $order->delivery['postcode']), 0, 5);
        $request = '<RateV4Request USERID="' . MODULE_SHIPPING_USPS_USERID . '">' . '<Revision>2</Revision>';
        $package_count = 0;
        $service = '';
        
        foreach (explode(',', MODULE_SHIPPING_USPS_TYPES) as $request_type) {
          $request_type = trim( $request_type );
          if (is_numeric($request_type) || preg_match('#International#', $request_type))
            continue;
          $first_class_type = '';
          $container = 'VARIABLE';
          if( preg_match( '#First\-Class#', $request_type ) && $this->usps_weight <= 13/16 ) {
            $service = 'First Class';
            if ($request_type == 'First-Class MailRM Large Envelope') {
              $first_class_type = '<FirstClassMailType>FLAT</FirstClassMailType>';
            } else {
              $first_class_type = '<FirstClassMailType>PARCEL</FirstClassMailType>';
            }
          } elseif ($request_type == 'Media MailRM') {
            $service = 'MEDIA';
          } elseif ($request_type == 'Standard PostRM') {
            $service = 'PARCEL';
          } elseif (preg_match('#Priority MailTM#', $request_type)) {
            $service = 'PRIORITY COMMERCIAL';
            if ($request_type == 'Priority MailTM Flat Rate Envelope') {
              $container = 'FLAT RATE ENVELOPE';
            } elseif ($request_type == 'Priority MailTM Legal Flat Rate Envelope') {
              $container = 'LEGAL FLAT RATE ENVELOPE';
            } elseif ($request_type == 'Priority MailTM Padded Flat Rate Envelope') {
              $container = 'PADDED FLAT RATE ENVELOPE';
            } elseif ($request_type == 'Priority MailTM Small Flat Rate Box') {
              $container = 'SM FLAT RATE BOX';
            } elseif ($request_type == 'Priority MailTM Medium Flat Rate Box') {
              $container = 'MD FLAT RATE BOX';
            } elseif ($request_type == 'Priority MailTM Large Flat Rate Box') {
              $container = 'LG FLAT RATE BOX';
            } elseif ($request_type == 'Priority MailTM Regional Rate Box A') {
              $container = 'REGIONALRATEBOXA';
            } elseif ($request_type == 'Priority MailTM Regional Rate Box B') {
              $container = 'REGIONALRATEBOXB';
            } elseif ($request_type == 'Priority MailTM Regional Rate Box C') {
              $container = 'REGIONALRATEBOXC';
            }
          } elseif (preg_match('#Priority Mail ExpressTM#', $request_type)) {
            $service = 'EXPRESS COMMERCIAL';
            if ($request_type == 'Priority Mail ExpressTM Flat Rate Envelope') {
              $container = 'FLAT RATE ENVELOPE';
            } elseif ($request_type == 'Priority Mail ExpressTM Legal Flat Rate Envelope') {
              $container = 'LEGAL FLAT RATE ENVELOPE';
            } elseif ($request_type == 'Priority Mail ExpressTM Flat Rate Boxes') {
              $container = 'FLAT RATE BOX';
            }
          } else {
            continue;
          }
            
          // Create XML for this package using settings determined above
          $request .= '<Package ID="' . $package_count . '">' .
            '<Service>' . $service . '</Service>' .
            $first_class_type .
            '<ZipOrigination>' . SHIPPING_ORIGIN_ZIP . '</ZipOrigination>' .
            '<ZipDestination>' . $ZipDestination . '</ZipDestination>' .
            '<Pounds>' . $this->pounds . '</Pounds>' .
            '<Ounces>' . $this->ounces . '</Ounces>' .
            '<Container>' . $container . '</Container>' .
            '<Size>REGULAR</Size>' .
            '<Machinable>TRUE</Machinable>' .
            '</Package>';
          $package_count++;
        }
        
        $request .= '</RateV4Request>';
        $request = 'API=RateV4&XML=' . urlencode($request);
        
      } else {
        //International delivery
        $request = '<IntlRateV2Request USERID="' . MODULE_SHIPPING_USPS_USERID . '">' .
        '<Revision>2</Revision>' .
        '<Package ID="0">' .
        '<Pounds>' . $this->pounds . '</Pounds>' .
        '<Ounces>' . $this->ounces . '</Ounces>' .
        '<MailType>All</MailType>' .
        '<GXG>' .
        '<POBoxFlag>N</POBoxFlag>' .
        '<GiftFlag>N</GiftFlag>' .
        '</GXG>' .
        '<ValueOfContents>' . ($order->info['subtotal'] + $order->info['tax']) . '</ValueOfContents>' .
        '<Country>' . tep_get_country_name($order->delivery['country']['id']) . '</Country>' .
        '<Container>RECTANGULAR</Container>' .
        '<Size>LARGE</Size>' .
        '<Width>2</Width>' .
        '<Length>10</Length>' .
        '<Height>6</Height>' .
        '<Girth>29</Girth>' .
        '<OriginZip>' . SHIPPING_ORIGIN_ZIP . '</OriginZip>' .

        // Changed N to Y to activate optional commercial base pricing for international services - 01/27/13 a.forever edit
        '<CommercialFlag>Y</CommercialFlag>' .
        '<ExtraServices>' .
        '<ExtraService>0</ExtraService>' .
        '<ExtraService>1</ExtraService>' .
        '<ExtraService>2</ExtraService>' .
        '<ExtraService>3</ExtraService>' .
        '<ExtraService>5</ExtraService>' .
        '<ExtraService>6</ExtraService>' .
        '</ExtraServices>' .
        '</Package>' .
        '</IntlRateV2Request>';
        $request = 'API=IntlRateV2&XML=' . urlencode($request);
      }

// Make quote-request to production USPS servers

      $usps_server = 'http://production.shippingapis.com';
      $api_dll = 'shippingapi.dll';

    $body = '';

if (_is_curl_installed()) {
// BOF CURL
    // Send quote request via CURL

    global $request_type;
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $usps_server . '/' . $api_dll);
    curl_setopt($ch, CURLOPT_REFERER, ($request_type == 'SSL' ? HTTPS_SERVER . DIR_WS_HTTPS_CATALOG : HTTP_SERVER . DIR_WS_CATALOG ));
    curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_VERBOSE, 0);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_SSLVERSION, 3);
    curl_setopt($ch, CURLOPT_USERAGENT, 'oscommerce-based');
    if (CURL_PROXY_REQUIRED == 'True') {
      $this->proxy_tunnel_flag = (defined('CURL_PROXY_TUNNEL_FLAG') && strtoupper(CURL_PROXY_TUNNEL_FLAG) == 'FALSE') ? false : true;
      curl_setopt ($ch, CURLOPT_HTTPPROXYTUNNEL, $this->proxy_tunnel_flag);
      curl_setopt ($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
      curl_setopt ($ch, CURLOPT_PROXY, CURL_PROXY_SERVER_DETAILS);
    }

    $body = curl_exec($ch);
    $this->commError = curl_error($ch);
    $this->commErrNo = curl_errno($ch);
    $this->commInfo = @curl_getinfo($ch);
    curl_close ($ch);
    if ($this->commErrNo != 0) return -1;
// EOF CURL
$response = $body;

} else {

      // Connect via old http_client to the USPS server and retrieve a quote
      if (!class_exists('httpClient', FALSE)) require_once(DIR_WS_CLASSES . 'http_client.php');
      $http = new httpClient();
      if ($http->Connect($usps_server, 80)) {
        $http->addHeader('Host', $usps_server);
        $http->addHeader('User-Agent', 'Loaded Commerce');
        $http->addHeader('Connection', 'Close');
        if ($http->Get('/' . $api_dll . '?' . $request)) $body = $http->getBody();
          $response = decode_chunked( $http->getBody() );

        $http->Disconnect();
         } else {
        return -1;
        }
      } // false

        $body = preg_replace( array(
          '{&lt;sup&gt;&#174;&lt;/sup&gt;}',  /* Registered Trademark symbol - July 2013 update */
          '{&lt;sup&gt;&#8482;&lt;/sup&gt;}',  /* Trademark symbol - July 2013 update */
          '/<br>/'
        ), array (
          'RM',
          'TM',
          'BREAK'
        ), htmlspecialchars_decode($response));
        
   //$mail_body = "Request:\n" . urldecode($request) . "\n\nResponse:\n" . $body;
   //mail( 'devi@egcms.com', STORE_OWNER, $mail_body );
        
        $response_array = json_decode(json_encode(simplexml_load_string($body)), TRUE);
        $response_array = $this->clean_services( $response_array );
        
        return $response_array;
    }

    ////
    // This method was written for the July 2013 API changes
    // It removes the "Delivery Date" code from the MailService names
    // The "Delivery Date" codes are added to the Response array for future use
    function clean_services( $response_array ) {
      global $order;
      
      // Scrub these out of the MailService names
      $pattern = array (
        '/ 1-Day/i',
        '/ 2-Day/i',
        '/ 3-Day/i',
        '/ Military/i',
        '/ DPO/i'
      );
      
      // A single quote is a special case
      if (isset ($response_array['Package']['Postage']) && tep_not_null($response_array['Package']['Postage'])) {
        $service = $response_array['Package']['Postage']['MailService'];
        $temp_service = preg_replace( $pattern, '', $service );
        $response_array['Package']['Postage']['MailService'] = preg_replace( '/\s+/', ' ', $temp_service );
        $response_array['Package']['Postage']['DeliveryDays'] = $this->get_delivery_days( $pattern, $service );
      } else {  // Step through all of the quotes
        $count_services = count( $response_array['Package'] );
        for( $index=0; $index<$count_services; $index++ ) {
          $service = $response_array['Package'][$index]['Postage']['MailService'];
          // First Class service hack, because USPS isn't returning what they say they do.
          // Remove this block if they ever get their act together. 
          if( $service == 'First-Class Mail' ) {
            $first_class_type = $response_array['Package'][$index]['FirstClassMailType'];
            switch( $first_class_type ) {
              case 'FLAT' :
                $service .= 'RM Large Envelope';
                break;
                
              case 'PARCEL' :
                $service .= 'RM Parcel';
                break;
                
              default :
                break;
            }
          }
          // End hack
          $temp_service = preg_replace( $pattern, '', $service );
          $response_array['Package'][$index]['Postage']['MailService'] = preg_replace( '/\s+/', ' ', $temp_service );
          $response_array['Package'][$index]['Postage']['DeliveryDays'] = $this->get_delivery_days( $pattern, $service );
        }
      }
        
      return $response_array;
    }
    
    function install() {
      tep_db_query("ALTER TABLE `configuration` CHANGE `configuration_value` `configuration_value` TEXT NOT NULL, CHANGE `set_function` `set_function` TEXT NULL DEFAULT NULL");
      tep_db_query("update " . TABLE_CONFIGURATION . " SET configuration_value =  'true' where configuration_key = 'EMAIL_USE_HTML'");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable USPS Shipping', 'MODULE_SHIPPING_USPS_STATUS', 'True', 'Do you want to offer USPS shipping?', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Enter the USPS User ID', 'MODULE_SHIPPING_USPS_USERID', '371MILLE2051', 'Enter the USPS USERID assigned to you.', '6', '0', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Tax Class', 'MODULE_SHIPPING_USPS_TAX_CLASS', '0', 'Use the following tax class on the shipping fee.', '6', '0', 'tep_get_tax_class_title', 'tep_cfg_pull_down_tax_classes(', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Shipping Zone', 'MODULE_SHIPPING_USPS_ZONE', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '0', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort Order', 'MODULE_SHIPPING_USPS_SORT_ORDER', '0', 'Sort order of display.', '6', '0', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Shipping Methods (Domestic and International)',  'MODULE_SHIPPING_USPS_TYPES', '0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00', '<b><u>Checkbox:</u></b> Select the services to be offered<br><b><u>Minimum Weight (lbs)</u></b>first input field<br><b><u>Maximum Weight (lbs):</u></b>second input field<br><br>USPS returns methods based on cart weights.  These settings will allow further control (particularly helpful for flat rate methods) but will not override USPS limits', '6', '0', 'tep_cfg_usps_services(array(" . $this->get_usps_services_list() . "), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Extra Services (Domestic)', 'MODULE_SHIPPING_USPS_DMST_SERVICES', 'Certified MailRM, N, Insurance, N, Adult Signature Restricted Delivery, N, Registered without Insurance, N, Registered MailTM, N, Collect on Delivery, N, Return Receipt for Merchandise, N, Return Receipt, N, Certificate of Mailing, N, Express Mail Insurance, N, Delivery ConfirmationTM, N, Signature ConfirmationTM, N', 'Included in postage rates.  Not shown to the customer.', '6', '0', 'tep_cfg_usps_extraservices(array(\'Certified MailRM\', \'Insurance\', \'Adult Signature Restricted Delivery\', \'Registered without Insurance\', \'Registered MailTM\', \'Collect on Delivery\', \'Return Receipt for Merchandise\', \'Return Receipt\', \'Certificate of Mailing\', \'Express Mail Insurance\', \'Delivery ConfirmationTM\', \'Signature ConfirmationTM\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Extra Services (International)', 'MODULE_SHIPPING_USPS_INTL_SERVICES', 'Registered Mail, N, Insurance, N, Return Receipt, N, Restricted Delivery, N, Pick-Up, N, Certificate of Mailing, N', 'Included in postage rates.  Not shown to the customer.', '6', '0', 'tep_cfg_usps_extraservices(array(\'Registered Mail\', \'Insurance\', \'Return Receipt\', \'Restricted Delivery\', \'Pick-Up\', \'Certificate of Mailing\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Retail pricing or Online pricing?', 'MODULE_SHIPPING_USPS_RATE_TYPE', 'Online', 'Rates will be returned ONLY for methods available in this pricing type.  Applies to prices <u>and</u> add on services', '6', '0', 'tep_cfg_select_option(array(\'Retail\', \'Online\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Rates Sort Order:', 'MODULE_SHIPPING_USPS_RATE_SORTER', 'Ascending', 'Ascending: Low to High<br>Descending: High to Low', '6', '0', 'tep_cfg_select_option(array(\'Ascending\', \'Descending\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Show International Regulations:', 'MODULE_SHIPPING_USPS_REGULATIONS', 'True', 'Displays international regulations and customs information.', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
      tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Show Weights', 'MODULE_SHIPPING_USPS_WEIGHTS', 'True', 'Displays the package weight on the quotes.', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
    }
    
    function keys() {
      return array (
        'MODULE_SHIPPING_USPS_STATUS',
        'MODULE_SHIPPING_USPS_USERID',
        'MODULE_SHIPPING_USPS_TAX_CLASS',
        'MODULE_SHIPPING_USPS_ZONE',
        'MODULE_SHIPPING_USPS_SORT_ORDER',
        'MODULE_SHIPPING_USPS_TYPES',
        'MODULE_SHIPPING_USPS_DMST_SERVICES',
        'MODULE_SHIPPING_USPS_INTL_SERVICES',
        'MODULE_SHIPPING_USPS_RATE_TYPE',
        'MODULE_SHIPPING_USPS_RATE_SORTER',
        'MODULE_SHIPPING_USPS_REGULATIONS',
        'MODULE_SHIPPING_USPS_WEIGHTS'
      );
    }
    
    function remove() {
      tep_db_query("delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode("', '", $this->keys()) . "')");
    }
    
    function check() {
      if (!isset ($this->_check)) {
        $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_SHIPPING_USPS_STATUS'");
        $this->_check = tep_db_num_rows($check_query);
      }
      return $this->_check;
    }
    
    function get_delivery_days( $delivery_array, $service ) {
      $return = NULL;
      foreach( $delivery_array as $delivery ) {
        if( strpos( $service, $delivery) !== false ) {
          $return = $delivery;
          break;
        }
      }
      return $return;
    }
  
    ////
    // List of services
    function get_usps_services_list() {
      $services_list = "
        \'First-Class MailRM Large Envelope\', 
        \'First-Class MailRM Parcel\', 
        \'Media MailRM\', 
        \'Standard PostRM\', 
        \'Priority MailTM\', 
        \'Priority MailTM Flat Rate Envelope\', 
        \'Priority MailTM Legal Flat Rate Envelope\', 
        \'Priority MailTM Padded Flat Rate Envelope\', 
        \'Priority MailTM Small Flat Rate Box\', 
        \'Priority MailTM Medium Flat Rate Box\', 
        \'Priority MailTM Large Flat Rate Box\', 
        \'Priority MailTM Regional Rate Box A\', 
        \'Priority MailTM Regional Rate Box B\', 
        \'Priority MailTM Regional Rate Box C\', 
        \'Priority Mail ExpressTM\', 
        \'Priority Mail ExpressTM Flat Rate Envelope\', 
        \'Priority Mail ExpressTM Legal Flat Rate Envelope\', 
        \'Priority Mail ExpressTM Flat Rate Boxes\', 
        \'First-Class MailRM International Large Envelope**\', 
        \'First-Class Package International ServiceTM\', 
        \'Priority Mail InternationalRM\', 
        \'Priority Mail InternationalRM Flat Rate Envelope\', 
        \'Priority Mail InternationalRM Small Flat Rate Box**\', 
        \'Priority Mail InternationalRM Medium Flat Rate Box\', 
        \'Priority Mail InternationalRM Large Flat Rate Box\', 
        \'Priority Mail Express InternationalTM\', 
        \'Priority Mail Express InternationalTM Flat Rate Envelope\', 
        \'Priority Mail Express InternationalTM Flat Rate Boxes\', 
        \'USPS GXGTM Envelopes\', 
        \'Global Express GuaranteedRT (GXG)\'
      ";
      
      return preg_replace( '/\s+/', ' ', $services_list );
    }
    
  } // eof class

  ////
  //
  if( !function_exists( 'tep_cfg_usps_services' ) ) {
    function tep_cfg_usps_services($select_array, $key_value, $key = '') {
      $key_values = explode(", ", $key_value);
      $name = (($key) ? 'configuration[' . $key . '][]' : 'configuration_value');
      $string = '<b><div style="width:20px;float:left;text-align:center;">&nbsp;</div><div style="width:30px;float:left;text-align:center;">Min</div><div style="width:30px;float:left;text-align:center;">Max</div><div style="float:left;"></div><div style="width:50px;float:right;text-align:center;">Handling</div></b><div style="clear:both;"></div>';
      for ($i = 0; $i < sizeof($select_array); $i++) {
        $string .= '<div id="' . $key . $i . '">';
        $string .= '<div style="width:20px;float:left;text-align:center;">' . tep_draw_checkbox_field($name, $select_array[$i], (in_array($select_array[$i], $key_values) ? 'CHECKED' : '')) . '</div>';
        if (in_array($select_array[$i], $key_values))
          next($key_values);
        $string .= '<div style="width:30px;float:left;text-align:center;">' . tep_draw_input_field($name, current($key_values), 'size="1"') . '</div>';
        next($key_values);
        $string .= '<div style="width:30px;float:left;text-align:center;">' . tep_draw_input_field($name, current($key_values), 'size="1"') . '</div>';
        next($key_values);
        $string .= '<div style="float:left;">' . preg_replace(array (
          '/RM/',
          '/TM/',
          '/International/',
          '/Envelope/',
          '/ Mail/',
          '/Large/',
          '/Medium/',
          '/Small/',
          '/First/',
          '/Legal/',
          '/Padded/',
          '/Flat Rate/',
          '/Regional Rate/',
          '/Express Guaranteed /'
        ), array (
          '',
          '',
          'Int\'l',
          'Env',
          '',
          'Lg.',
          'Md.',
          'Sm.',
          '1st',
          'Leg.',
          'Pad.',
          'F/R',
          'R/R',
          'Exp Guar'
        ), $select_array[$i]) . '</div>';
        $string .= '<div style="width:50px;float:right;text-align:center;">$' . tep_draw_input_field($name, current($key_values), 'size="2"') . '</div>';
        next($key_values);
        $string .= '<div style="clear:both;"></div></div>';
      }
      return $string;
    }
  }
  
  if( !function_exists( 'tep_cfg_usps_extraservices' ) ) {
    function tep_cfg_usps_extraservices($select_array, $key_value, $key = '') {
      $key_values = explode(", ", $key_value);
      $name = (($key) ? 'configuration[' . $key . '][]' : 'configuration_value');
      $string = '<b><div style="width:20px;float:left;text-align:center;">N</div><div style="width:20px;float:left;text-align:center;">S</div><div style="width:20px;float:left;text-align:center;">H</div></b><div style="clear:both;"></div>';
      for ($i = 0; $i < sizeof($select_array); $i++) {
        $string .= tep_draw_hidden_field($name, $select_array[$i]);
        next($key_values);
        $string .= '<div id="' . $key . $i . '">';
        $string .= '<div style="width:20px;float:left;text-align:center;"><input type="checkbox" name="' . $name . '" value="N" ' . (current($key_values) == 'N' || current($key_values) == '' ? 'CHECKED' : '') . ' id="N" onClick="if($(this).is(\':checked\')) $(\'#C, #S, #H\', $(\'#' . $key . $i . '\')).removeAttr(\'checked\'); if($(\':checkbox:checked\', $(\'#' . $key . $i . '\')).size() == 0) $(this).attr(\'checked\', \'checked\');"></div>';
        $string .= '<div style="width:20px;float:left;text-align:center;"><input type="checkbox" name="' . $name . '" value="S" ' . (current($key_values) == 'S' ? 'CHECKED' : '') . ' id="S" onClick="if($(this).is(\':checked\')) $(\'#N, #C, #H\', $(\'#' . $key . $i . '\')).removeAttr(\'checked\'); if($(\':checkbox:checked\', $(\'#' . $key . $i . '\')).size() == 0) $(\'#N\', $(\'#' . $key . $i . '\')).attr(\'checked\', \'checked\');"></div>';
        $string .= '<div style="width:20px;float:left;text-align:center;"><input type="checkbox" name="' . $name . '" value="H" ' . (current($key_values) == 'H' ? 'CHECKED' : '') . ' id="H" onClick="if($(this).is(\':checked\')) $(\'#N, #C, #S\', $(\'#' . $key . $i . '\')).removeAttr(\'checked\'); if($(\':checkbox:checked\', $(\'#' . $key . $i . '\')).size() == 0) $(\'#N\', $(\'#' . $key . $i . '\')).attr(\'checked\', \'checked\');"></div>';
        next($key_values);
        $string .= preg_replace(array (
          '/Signature/',
          '/without/',
          '/Merchandise/',
          '/TM/',
          '/RM/'
        ), array (
          'Sig',
          'w/out',
          'Merch.',
          '',
          ''
        ), $select_array[$i]) . '<br>';
        $string .= '<div style="clear:both;"></div></div>';
      }
      return $string;
    }
  }
  /**
   * check to see if curl is installed in exntensions loaded
   */
  function _is_curl_installed() {
    if  (in_array  ('curl', get_loaded_extensions())) {
        return true;
    }
  }
  /**
   * dechunk an http 'transfer-encoding: chunked' message
   *
   * @[member='param'] string $chunk the encoded message
   * @[member='Return'] string the decoded message. If $chunk wasn't encoded properly it will be returned unmodified.
   */
  function decode_chunked($chunk) {
    $pos = 0;
    $len = strlen($chunk);
    $dechunk = null;
    while(($pos < $len)
        && ($chunkLenHex = substr($chunk,$pos, ($newlineAt = strpos($chunk,"\n",$pos+1))-$pos)))
    {
      if (!is_hex($chunkLenHex)) {
        trigger_error('Value is not properly chunk encoded', E_USER_WARNING);
        return $chunk;
      }
      $pos = $newlineAt + 1;
      $chunkLen = hexdec(rtrim($chunkLenHex,"\r\n"));
      $dechunk .= substr($chunk, $pos, $chunkLen);
      $pos = strpos($chunk, "\n", $pos + $chunkLen) + 1;
    }
    return $dechunk;
  }
  /**
   * determine if a string can represent a number in hexadecimal
   *
   * @[member='param'] string $hex
   * @[member='Return'] boolean true if the string is a hex, otherwise false
   */
  function is_hex($hex) {
    // regex is for weenies
    $hex = strtolower(trim(ltrim($hex,"0")));
    if (empty($hex)) { $hex = 0; };
    $dec = hexdec($hex);
    return ($hex == dechex($dec));
  }
?>
