<?php

/**
 * Definiert Konstanten fuer Umgang mit dem UserAgent.
 *
 * <i>void defAgentConstants([mixed mobile_ids [, bool landscape_mode]])</i>
 *
 * Ermittelt Browser-Typ (mobile / non-mobile) sowie Breite, Hoehe und Farbtiefe
 * des Browser-Displays durch Auswertung entsprechender Daten aus
 *  - spezifischen Request-Headern oder
 *  - User-Agent-String (<b>ggf. $_SERVER['HTTP_USER_AGENT'] vorher initialisieren!</b>) oder
 *  - spezifischem URL-Parameter
 *
 * @version 080516
 * @since 060505
 * @author Cybaer <cybaer@binon.net>
 * @copyright Copyright (c) 2006-08 Cybaer
 * @license CC <http://creativecommons.org/licenses/by-nc-sa/2.5/>
 *
 * @category SYSTEM_DEFINITION
 * @access public
 *
 * @uses ifsetor()
 * @uses instr()
 * @uses from()
 *
 * @param mixed $mMobileIDs Ein Array oder (Pipe-separierter) String mit (Ausschnitten aus) UserAgent-Kennungen mobiler Clients
 * @param bool $bLandscapeMode Wenn Abmessungen fuer Landscape-Modus (iPhone) ermittelt werden sollen TRUE, Portrait-Modus FALSE (Default)
 * Example: <code><?php
 *  // Windows Mobile agent info by request header, i.e. 240x320 with 8 bit monochrome:
 *  // $_SERVER['HTTP_UA_PIXELS'] is "240x320";
 *  // $_SERVER['HTTP_UA_COLOR'] is "mono8";
 *  // or OpenWave info by request header, i.e. 240x320 with 16 bit color:
 *  // $_SERVER['HTTP_X_UP_DEVCAP_SCREENPIXELS'] is "240,320";
 *  // $_SERVER['HTTP_X_UP_DEVCAP_SCREENDEPTH'] is "16";
 *  // $_SERVER['HTTP_X_UP_DEVCAP_ISCOLOR'] is "1";
 *  // or other agent info by request header, i.e. 240x320 with 18 bit color:
 *  // $_SERVER['HTTP_X_JPHONE_DISPLAY'] is "240*320";
 *  // $_SERVER['HTTP_X_JPHONE_COLOR'] is "C262144";
 *  // or
 *  // $_SERVER['HTTP_X_OS_PREFS'] is "fw:240; fh:320; cd:18c; pl:3; pj:1; pa:1;pi:1;ps:0;"
 *  // or agent info by User-Agent: header, i.e. 240x320 with 16 bit display:
 *  // $_SERVER['HTTP_USER_AGENT'] contains "240x320x16" or "16;240x320"
 *  // or agent info by URL parameter, i.e. 1 bit // 4 bit monochrome // 16 bit color:
 *  // myscript.php?AGENT_DISPLAY=240x320x1 // 240x230x4mono // 240x230x16color
 *  echo ((MOBILE_AGENT)?'Mobile ':'').AGENT_WIDTH.'x'.AGENT_HEIGHT.' '.((AGENT_MONO)?'monochrome':'color').' display with '.((AGENT_DEPTH<32)?(((!AGENT_DEPTH)?'unknown':pow(2,AGENT_DEPTH)).' colors'):'truecolor').'.<br>';
 *  echo 'Client environment: '.ifsetor(AGENT_WIDTH,'?').'x'.ifsetor(AGENT_HEIGHT,'?').' pixel ('.((MOBILE_AGENT===FALSE)?'non-':'').'mobile'.((MOBILE_AGENT===NULL)?'?':'').' device</span> with <span title="Constant: AGENT_DEPTH">'.ifsetor(AGENT_DEPTH,'?').' bit</span> <span title="Constant: AGENT_MONO">'.((AGENT_MONO)?'monochrome':'color').((AGENT_MONO===NULL)?'?':'').' display</span>)<br>';
 * ?></code>
 */
function defAgentConstants($mMobileIDs=NULL,$bLandscapeMode=TRUE) {
 
// UA-Kennstrings (s. http://www.zytrax.com/tech/web/mobile_ids.html ) - Nintendo Wii wird als Mobilgeraet gewertet
 
if(empty($mMobileIDs)) { $mMobileIDs='Windows CE|PIE|Pocket|Mobile|Portable|portal|Smartphone|SDA|PDA|Handheld|Symbian|EPOC|WAP|Palm|Plucker|Blazer|CHTML|CLDC|RegKing|ReqwirelessWeb|Netfront|UP.Browser|UP.Link|BlackBerry|Minimo|Opera Mini|Nitro|PS2|Wii|OPWV|Xiino|SEC-SGH|Nokia|Vodafone|J-PHONE|DoCoMo|Avantgo'; }
 
// Aufloesungsgrenze fuer Definition "Mobilgeraet" (PDA) festlegen (sofern nicht bereits vorher geschehen)
 
@define('MOBILE_MAX_WIDTH',800);
 @
define('MOBILE_MAX_HEIGHT',600);
 
// Maximale Bitanzahl festlegen, bis zu der das Display als "monochrome" eingestuft werden soll
 
@define('MONO_MAX_DEPTH',4);

 
// Wenn UserAgent seine Aufloesung nicht in HTTP_UA_PIXELS sendet ...
 
if(empty($_SERVER['HTTP_UA_PIXELS'])) {

  
// ... Alternativen versuchen (und Ergebnis einheitlich nach HTTP_UA_PIXELS):
  
if(!empty($_SERVER['HTTP_X_OS_PREFS'])) {
   if(
instr($_SERVER['HTTP_X_OS_PREFS'],'fw:|fh:',0,TRUE,TRUE,TRUE)) {
    
$_SERVER['HTTP_UA_PIXELS']=intval(substr($_SERVER['HTTP_X_OS_PREFS'],strpos($_SERVER['HTTP_X_OS_PREFS'],'fw:')+3)).'x'.intval(substr($_SERVER['HTTP_X_OS_PREFS'],strpos($_SERVER['HTTP_X_OS_PREFS'],'fh:')+3));
   }
   
$_LOCAL_['UA_DEPTH_POS']=strpos($_SERVER['HTTP_X_OS_PREFS'],'cd:');
   if(
$_LOCAL_['UA_DEPTH_POS']!==FALSE) {
    
$_LOCAL_['UA_DEPTH']=substr($_SERVER['HTTP_X_OS_PREFS'],$_LOCAL_['UA_DEPTH_POS']+3,strpos($_SERVER['HTTP_X_OS_PREFS'],';',$_LOCAL_['UA_DEPTH_POS'])-$_LOCAL_['UA_DEPTH_POS']-3);
    
$_SERVER['HTTP_UA_COLOR']=((substr($_LOCAL_['UA_DEPTH'],-1)=='c')?'color':'mono').intval(substr($_SERVER['HTTP_X_OS_PREFS'],strpos($_SERVER['HTTP_X_OS_PREFS'],'cd:')+3));
   }
  } else if(!empty(
$_SERVER['HTTP_X_UP_DEVCAP_SCREENPIXELS']) || !empty($_SERVER['HTTP_X_JPHONE_DISPLAY']) || !empty($_SERVER['HTTP_X_AVANTGO_SCREENSIZE'])) {
   
// AvantGo-Header dekodieren
   
if(!empty($_SERVER['HTTP_X_AVANTGO_SCREENSIZE'])) { $_SERVER['HTTP_X_AVANTGO_SCREENSIZE']=base64_decode($_SERVER['HTTP_X_AVANTGO_SCREENSIZE']); }
   
// Weitere moegliche Pixel-Header (Matches: 2 Zahlen getrennt durch Nicht-Ziffern)
   
if(preg_match('/([\d]*)[^\d]*([\d]*)/',@ifsetor($_SERVER['HTTP_X_UP_DEVCAP_SCREENPIXELS'],@ifsetor($_SERVER['HTTP_X_JPHONE_DISPLAY'],@ifsetor($_SERVER['HTTP_X_AVANTGO_SCREENSIZE']))),$_LOCAL_['UA_PIXELS'])) {
    
$_SERVER['HTTP_UA_PIXELS']=$_LOCAL_['UA_PIXELS'][1].'x'.$_LOCAL_['UA_PIXELS'][2];
   }
   
// Zusaetzlich ggf. fehlende J-Phone/Vodafone-Farbtiefe setzen
   
if(!empty($_SERVER['HTTP_X_JPHONE_DISPLAY']) && empty($_SERVER['HTTP_X_JPHONE_COLOR'])) {
    
$_SERVER['HTTP_X_JPHONE_COLOR']='M2';
   }

  
// Allgemeine UA-Kennung (Matches: "1024x1600" & "240x320" | Not: "16x01024" & "10240x16")
  
} else if(preg_match('/[\d]{3,4}x[\d]{3,4}/',$_SERVER['HTTP_USER_AGENT'],$_LOCAL_['UA_PIXELS'])) {
   
$_SERVER['HTTP_UA_PIXELS']=$_LOCAL_['UA_PIXELS'][0];

  
// iPhone-UA-Kennung
  
} else if(instr($_SERVER['HTTP_USER_AGENT'],array('iPhone','iPod'))) {
   
$_SERVER['HTTP_UA_PIXELS']=($bLandscapeMode)?'480x196':'320x356'// Portrait-Mode ohne Keyboard (mit Keyboard: 320x140)
   
$_SERVER['HTTP_UA_COLOR']='color16';

   
// Alternativ in REQUEST definierte Daten verwenden (z.B. "?AGENT_DISPLAY=240x320x16color")
  
} elseif(!empty($_REQUEST['AGENT_DISPLAY'])) {
   
// Agent-Display-Parameter aufteilen
   
$_LOCAL_['AD_PARAM']=explode('x',$_REQUEST['AGENT_DISPLAY']);
   
// Abmessungen in Parameter 1 und 2
   
$_SERVER['HTTP_UA_PIXELS']=$_LOCAL_['AD_PARAM'][0].'x'.$_LOCAL_['AD_PARAM'][1];
   
// Bittiefe und Farbigkeit in Parameter 3
   
$_SERVER['HTTP_UA_COLOR']=$_LOCAL_['AD_PARAM'][2];
  }

 }

 
// Abmessungen definieren
 
define('AGENT_WIDTH',(!empty($_SERVER['HTTP_UA_PIXELS']))?intval($_SERVER['HTTP_UA_PIXELS']):NULL);
 
define('AGENT_HEIGHT',(!empty($_SERVER['HTTP_UA_PIXELS']))?intval(from($_SERVER['HTTP_UA_PIXELS'],'x',1)):NULL);


 
// Wenn UserAgent keine Farbinformation in HTTP_UA_COLOR sendet ...
 
if(AGENT_WIDTH && AGENT_HEIGHT && empty($_SERVER['HTTP_UA_COLOR'])) {

  
// ... Alternativen versuchen (und einheitlich in HTTP_UA_COLOR sichern):
  
if(!empty($_SERVER['HTTP_X_UP_DEVCAP_SCREENDEPTH'])) {
   
$_LOCAL_['UA_DEPTH']=intval($_SERVER['HTTP_X_UP_DEVCAP_SCREENDEPTH']);
   
$_SERVER['HTTP_UA_COLOR']=((@ifsetor($_SERVER['HTTP_X_UP_DEVCAP_ISCOLOR'],0,1,0))?'color':'mono').$_LOCAL_['UA_DEPTH'];

  } else if(!empty(
$_SERVER['HTTP_X_JPHONE_COLOR'])) {
   
$_LOCAL_['UA_DEPTH']=strlen(decbin(intval(substr($_SERVER['HTTP_X_JPHONE_COLOR'],1))-1));
   
$_SERVER['HTTP_UA_COLOR']=((strtoupper($_SERVER['HTTP_X_JPHONE_COLOR'][0])=='C')?'color':'mono').$_LOCAL_['UA_DEPTH'];

  
// AvantGo-Alternative in $_LOCAL_['UA_DEPTH'] sichern
  
} else if(!empty($_SERVER['HTTP_X_AVANTGO_COLORDEPTH'])) {
   
$_LOCAL_['UA_DEPTH']=base64_decode($_SERVER['HTTP_X_AVANTGO_COLORDEPTH']);

  
// UA-Kennung (Matches: " 240x320x16" & "PPC; 16;1024x1600" & "PPC;8;320x320" | Not: "16x240x320" & "PPC128;160x64")
  
} else if(preg_match('/[\s|;]{1}[\d]{0,2};{0,1}[\d]{3,4}x[\d]{3,4}x{0,1}[\d]{0,2}/',$_SERVER['HTTP_USER_AGENT'],$_LOCAL_['UA_COLOR'])) {
   
$_LOCAL_['UA_DEPTH']=$_LOCAL_['UA_COLOR'][0];

  
// Alternativ in REQUEST definierte Daten verwenden (z.B. "?AGENT_DISPLAY=240x320x16color")
  
} elseif(!empty($_REQUEST['AGENT_DISPLAY'])) {
   
// Farbklassifizierung ("color"/"mono") aus REQUEST entfernen und einer neuen Kennung voranstellen
   
if(instr($_REQUEST['AGENT_DISPLAY'],'color',0,FALSE)) {
    
$_LOCAL_['UA_DEPTH']='color';
    
$_REQUEST['AGENT_DISPLAY']=str_replace('color','',$_REQUEST['AGENT_DISPLAY']);
   } elseif(
instr($_REQUEST['AGENT_DISPLAY'],'mono',0,FALSE)) {
    
$_LOCAL_['UA_DEPTH']='mono';
    
$_REQUEST['AGENT_DISPLAY']=str_replace('mono','',$_REQUEST['AGENT_DISPLAY']);
   } else {
    
$_LOCAL_['UA_DEPTH']='';
   }
   
$_LOCAL_['UA_DEPTH'].=$_REQUEST['AGENT_DISPLAY'];
  }

  
// (Temporaere) Variablen vereinheitlichen
  
if($_LOCAL_['UA_DEPTH']) {
   if(
$_LOCAL_['UA_DEPTH']==@ifsetor($_REQUEST['AGENT_DISPLAY'])) {
    
$_SERVER['HTTP_UA_COLOR']=str_replace('x','',str_replace(AGENT_WIDTH.'x'.AGENT_HEIGHT,'',$_LOCAL_['UA_DEPTH']));
    
$_LOCAL_['UA_DEPTH']=intval(preg_replace('/\D/','',$_SERVER['HTTP_UA_COLOR']));
   } else {
    
$_LOCAL_['HTTP_UA_COLOR']=str_replace('x','',str_replace(AGENT_WIDTH.'x'.AGENT_HEIGHT,'',$_LOCAL_['UA_DEPTH']));
    
$_LOCAL_['UA_DEPTH']=intval(preg_replace('/\D/','',$_LOCAL_['UA_DEPTH']));
   }
  }

 } else if(!empty(
$_SERVER['HTTP_UA_COLOR'])) {
  
// HTTP_UA_COLOR existiert, temporaere Variable setzen
  
$_LOCAL_['UA_DEPTH']=intval(preg_replace('/\D/','',$_SERVER['HTTP_UA_COLOR']));
 }


 
// Bitanzahl des Displays definieren (wenn nicht ermittelbar: NULL)
 
define('AGENT_DEPTH',@ifsetor($_LOCAL_['UA_DEPTH'],NULL,'ifsetor',NULL));


 
// Ermitteln, ob das Display monochrome ist
 
if(!empty($_SERVER['HTTP_UA_COLOR'])) {
  
// Zuerst anhand der (im Header oder im REQUEST) vorhandenen Information ...
  
if(strpos($_SERVER['HTTP_UA_COLOR'],'mono')!==FALSE) {
   
$_LOCAL_['UA_MONO']=TRUE;
  } elseif(
strpos($_SERVER['HTTP_UA_COLOR'],'color')!==FALSE) {
   
$_LOCAL_['UA_MONO']=FALSE;
  } else {
   
// ... ansonsten anhand der ermittelten Bitanzahl
   
if(AGENT_DEPTH) {
    
$_LOCAL_['UA_MONO']=(AGENT_DEPTH<=MONO_MAX_DEPTH);
   } else {
    
$_LOCAL_['UA_MONO']=NULL;
   }
  }
 } else {
  
$_LOCAL_['UA_MONO']=NULL;
 }
 
// Monochrome-Status des Displays definieren (wenn nicht ermittelbar: NULL)
 
define('AGENT_MONO',$_LOCAL_['UA_MONO']);

 
// Typ (Mobilephone/PDA?) des UserAgents ermitteln (wenn nicht ermittelbar: FALSE)
 
define('MOBILE_AGENT',instr($_SERVER['HTTP_USER_AGENT'],$mMobileIDs,0,FALSE,TRUE) || (AGENT_WIDTH && AGENT_WIDTH<MOBILE_MAX_WIDTH) || (AGENT_HEIGHT && AGENT_HEIGHT<MOBILE_MAX_HEIGHT));
}

// ---------------------------------------------------------------------------

/**
 * Zeichenkette in String enthalten? Liefert Nummer des ersten gefundenen
 * Zeichens (Position+1) oder 0 im Fehlerfall.
 *
 * <i>int instr(string haystack, mixed needle [, int offset [, bool case-sensitive [, mixed separate [, bool all-needles]]]])</i>
 *
 * @version 070203
 * @since 060324
 * @author Cybaer <cybaer@binon.net>
 * @copyright Copyright (c) 2007 Cybaer
 *
 * @category STRING_LIBRARY
 * @access public
 *
 * @uses unset_array() Nur wenn $mNeedle ein Array oder aufzuteilender String ist
 * @uses instr() Ggf. rekursiver Aufruf - Sucharrays werden zerlegt in mehrere instr()-Aufrufe mit Suchstring
 *
 * @param string $sHaystack Zu durchsuchende Zeichenkette
 * @param mixed $mNeedle (string) Zu suchende Zeichenkette oder (int/double) zu suchende Zahl oder (array) Array mit Suchbegriffen
 * @param int $iOffset Startposition der Suche
 * @param bool $bCase Case-sensitive Suche TRUE/FALSE
 * @param mixed $mExplodeNeedle (FALSE): Suchbegriff wird in Sucharray aufgeteilt wenn wahr. Enthaelt das Trennzeichen, andernfalls ist das Trennzeichen "|"
 * @param bool $bAllNeedles Alle Suchbegriffe muessen enthalten sein TRUE/FALSE
 * @return int Die Nummer des ersten gefundenen Zeichens (Position+1) oder 0 im Fehlerfall.
 *
 * Example: <code><?php
 * echo instr('Die 1. IE-Version',1);                        // -> 5 (gesucht: "1" von Beginn an)
 * echo instr('Die 1. IE-Version','IE',0);                   // -> 8 (gesucht: "IE" von Beginn an)
 * echo instr('Die 1. IE-Version','ie',5,TRUE);              // -> 0 (gesucht: "ie" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie',5,FALSE);             // -> 8 (gesucht: "ie", "Ie", "iE" oder "IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie|IE',5,TRUE);           // -> 0 (gesucht: "ie|IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie|IE',5,TRUE,TRUE);      // -> 8 (gesucht: "ie" oder "IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie|IE',5,TRUE,TRUE,TRUE); // -> 0 (gesucht: "ie" und "IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie/IE',5,TRUE,'/');       // -> 8 (gesucht: "ie" oder "IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie IE',5,TRUE,TRUE);      // -> 0 (gesucht: "ie IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version','ie IE',5,TRUE,' ');       // -> 8 (gesucht: "ie" oder "IE" ab dem 6. Zeichen)
 * echo instr('Die 1. IE-Version',array('ie','IE'));         // -> 2 (gesucht: "ie" oder "IE" von Beginn an)
 * ?></code>
 */
function instr($sHaystack,$mNeedle,$iOffset=0,$bCase=TRUE,$mExplodeNeedle=FALSE,$bAllNeedles=FALSE) {

 
// Bei case-insensitive Suche den zu durchsuchenden String in Kleinbuchstaben wandeln
 
if(!$bCase) { $sHaystack=strtolower($sHaystack); }

 
// Mehrere moegliche Zeichenketten in einem String uebergeben
 
if($mExplodeNeedle!==FALSE && is_string($mNeedle)) {
  
// Wurde Trennzeichen uebergeben?
  
if(is_string($mExplodeNeedle) && strlen($mExplodeNeedle)) {
   
// Ja: String an uebergebenem Trennzeichen aufteilen
   
$mNeedle=explode($mExplodeNeedle,$mNeedle);
  } elseif(
$mExplodeNeedle!==NULL) {
   
// Nein: String an Default-Trennzeichen aufteilen (keine leeren Eintraege)
   
$mNeedle=explode('|',$mNeedle);
  }
 }

 
// Mehrere moegliche Zeichenketten in einem Array uebergeben?
 
if(is_array($mNeedle)) {
  
$iResult=0$iCount=0;
  
// Leere Sucheintraege entfernen
  
$mNeedle=unset_array($mNeedle,'');
  
// Ja: Array-Eintraege durchgehen
  
foreach($mNeedle as $mSearch) {
   
// Eintrag suchen (kann wieder Array sein)
   
$iSearch=instr($sHaystack,$mSearch,$iOffset,$bCase);
   
// Bei Erfolg:
   
if($iSearch) {
    
// Wenn alle Needles gefunden werden muessen
    
if($bAllNeedles) {
     
// Zaehler erhoehen ...
     
$iCount++;
     
// ... und erste Fundstelle sichern
     
if($iCount==1) { $iResult=$iSearch; }
    } else {
     
// Wenn nur eine Needle gefunden werden muss: Fundstelle sichern
     
$iResult=$iSearch;
     
// Suche abbrechen
     
break;
    }
   } else if(
$bAllNeedles) {
    
// Needle nicht gefunden obwohl verlangt: Abbruch
    
break;
   }
  }
  
// Wirklich alle gefunden?
  
if($bAllNeedles && $iCount<sizeof($mNeedle)) { $iResult=0; }
 } else {
  
// Kein Array als Needle uebergeben: Datentyp auf String setzen
  
settype($mNeedle,'string');
  
// Bei leerem String ...
  
if($mNeedle=='') {
   
// ... erst gar nicht suchen
   
$iResult=0;
  } else {
   
// Bei case-insensitive Suche den Suchstring in Kleinbuchstaben wandeln
   
if(!$bCase) { $mNeedle=strtolower($mNeedle); }
   
// Suchergebnis merken
   
$iResult=strpos($sHaystack,$mNeedle,$iOffset);
   
// Ergebnis ist 0 bei Nichterfolg und Position des Suchstrings +1 bei Erfolg
   
$iResult=($iResult===FALSE)?0:$iResult+1;
  }
 }
 return 
$iResult;
}

// ---------------------------------------------------------------------------

/* Gebe Variableninhalt oder Alternative zurueck (bei ggf. nicht existenten Variablen mit @ aufrufen!) 060323/060506 */
function ifsetor($mContent,$mNullReturn='',$mTrueReturn='ifsetor',$mFalseReturn='ifsetor') {
 if(
$mContent===NULL) {
  return 
$mNullReturn;
 } elseif(
$mContent) {
  return (
$mTrueReturn!='ifsetor')?$mTrueReturn:$mContent;
 } else {
  return (
$mFalseReturn!='ifsetor')?$mFalseReturn:$mContent;
 }
}

// ---------------------------------------------------------------------------

/* Loeschen eines Eintrags aus einem Array 060325/061106 */
function unset_array($aHaystack,$mNeedle,$bStrict=FALSE,$bRebuildNumArray=FALSE) {
 
$aResult=array();
 
$iCount=0;
 foreach(
$aHaystack as $sKey => $mValue) {
  if(
$bStrict) {
   if(
$mValue!==$mNeedle) { $aResult[($bRebuildNumArray)?$iCount:$sKey]=$mValue$iCount++; }
  } else {
   if(
$mValue!=$mNeedle) { $aResult[($bRebuildNumArray)?$iCount:$sKey]=$mValue$iCount++; }
  }
 }
 return 
$aResult;
}

// ---------------------------------------------------------------------------

/* Hole String ab needle */
function from($haystack,$needle,$offset=0) {
 
$result=strpos($haystack,$needle);
 
$result=substr($haystack,$result+$offset);
 return 
$result;
}

?>