From 15ae43bc55fffc8a9ccbf8215883532daf0d4a82 Mon Sep 17 00:00:00 2001 From: Borek Bernard Date: Mon, 27 Feb 2012 20:42:06 +0100 Subject: Colon is no longer rewritten to semicolon for IIS 7.x and for older versions of IIS, semicolon is displayed as is, not URL-encoded. --- inc/common.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index 0c769c50d..26904dd46 100644 --- a/inc/common.php +++ b/inc/common.php @@ -323,12 +323,14 @@ function idfilter($id,$ue=true){ if ($conf['useslash'] && $conf['userewrite']){ $id = strtr($id,':','/'); }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && - $conf['userewrite']) { + $conf['userewrite'] && + strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7.') === false) { $id = strtr($id,':',';'); } if($ue){ $id = rawurlencode($id); $id = str_replace('%3A',':',$id); //keep as colon + $id = str_replace('%3B',';',$id); //keep as semicolon $id = str_replace('%2F','/',$id); //keep as slash } return $id; -- cgit v1.2.3 From 9664a2b801ce91ae3707306f01fb4cad528b1258 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 14 Jul 2012 09:54:42 +0200 Subject: changed the link in the installer to point to wiki:welcome --- inc/lang/ar/lang.php | 4 ++-- inc/lang/az/lang.php | 4 ++-- inc/lang/bg/lang.php | 4 ++-- inc/lang/ca-valencia/lang.php | 4 ++-- inc/lang/ca/lang.php | 4 ++-- inc/lang/cs/lang.php | 4 ++-- inc/lang/da/lang.php | 4 ++-- inc/lang/de-informal/lang.php | 4 ++-- inc/lang/de/lang.php | 4 ++-- inc/lang/el/lang.php | 4 ++-- inc/lang/en/lang.php | 4 ++-- inc/lang/eo/lang.php | 4 ++-- inc/lang/es/lang.php | 4 ++-- inc/lang/et/lang.php | 4 ++-- inc/lang/eu/lang.php | 4 ++-- inc/lang/fa/lang.php | 4 ++-- inc/lang/fi/lang.php | 4 ++-- inc/lang/fr/lang.php | 4 ++-- inc/lang/gl/lang.php | 4 ++-- inc/lang/he/lang.php | 4 ++-- inc/lang/hr/lang.php | 4 ++-- inc/lang/hu/lang.php | 4 ++-- inc/lang/ia/lang.php | 4 ++-- inc/lang/id/lang.php | 4 ++-- inc/lang/it/lang.php | 4 ++-- inc/lang/ja/lang.php | 4 ++-- inc/lang/ko/lang.php | 4 ++-- inc/lang/la/lang.php | 4 ++-- inc/lang/lv/lang.php | 4 ++-- inc/lang/mk/lang.php | 4 ++-- inc/lang/mr/lang.php | 4 ++-- inc/lang/ne/lang.php | 4 ++-- inc/lang/nl/lang.php | 4 ++-- inc/lang/no/lang.php | 4 ++-- inc/lang/pl/lang.php | 4 ++-- inc/lang/pt-br/lang.php | 4 ++-- inc/lang/pt/lang.php | 4 ++-- inc/lang/ro/lang.php | 4 ++-- inc/lang/ru/lang.php | 4 ++-- inc/lang/sk/lang.php | 4 ++-- inc/lang/sl/lang.php | 4 ++-- inc/lang/sq/lang.php | 4 ++-- inc/lang/sr/lang.php | 4 ++-- inc/lang/sv/lang.php | 4 ++-- inc/lang/tr/lang.php | 4 ++-- inc/lang/uk/lang.php | 4 ++-- inc/lang/zh-tw/lang.php | 4 ++-- inc/lang/zh/lang.php | 4 ++-- 48 files changed, 96 insertions(+), 96 deletions(-) (limited to 'inc') diff --git a/inc/lang/ar/lang.php b/inc/lang/ar/lang.php index 350e26695..0f5566471 100644 --- a/inc/lang/ar/lang.php +++ b/inc/lang/ar/lang.php @@ -284,8 +284,8 @@ $lang['i_badhash'] = 'الملف dokuwiki.php غير مصنف أو (hash=%s)'; $lang['i_badval'] = 'القيمة %s غير شرعية أو فارغة'; $lang['i_success'] = 'الإعدادات تمت بنجاح، يرجى حذف الملف install.php الآن. -ثم تابع إلى دوكو ويكي الجديدة'; -$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; +ثم تابع إلى دوكو ويكي الجديدة'; +$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; $lang['i_policy'] = 'تصريح ACL مبدئي'; $lang['i_pol0'] = 'ويكي مفتوحة؛ أي القراءة والكتابة والتحميل مسموحة للجميع'; $lang['i_pol1'] = 'ويكي عامة؛ أي القراءة للجميع ولكن الكتابة والتحميل للمشتركين المسجلين فقط'; diff --git a/inc/lang/az/lang.php b/inc/lang/az/lang.php index fff6f34b7..384a347b7 100644 --- a/inc/lang/az/lang.php +++ b/inc/lang/az/lang.php @@ -208,8 +208,8 @@ $lang['i_writeerr'] = '%s yaradıla bilmədi. Faylın/qo $lang['i_badhash'] = 'dokuwiki.php tanıla bilmir və ya dəyişdirilmişdir (hash=%s)'; $lang['i_badval'] = '%s - səhv ya boş qiymətdir'; $lang['i_success'] = 'Konfiqurasiya uğurla başa çatdı. İndi siz install.php faylını silə bilərsiniz. - Yeni DokuWiki-nizə xoş gəlmişsiniz!'; -$lang['i_failure'] = 'Konfiqurasiya fayllarına məlumat yazan zaman səhvlər tapıldı. Yəgin ki, yeni DokuWiki-nizi istifadə etmədən öncə, Siz o xətaları əl ilə düzəltməli olacaqsınız.'; + Yeni DokuWiki-nizə xoş gəlmişsiniz!'; +$lang['i_failure'] = 'Konfiqurasiya fayllarına məlumat yazan zaman səhvlər tapıldı. Yəgin ki, yeni DokuWiki-nizi istifadə etmədən öncə, Siz o xətaları əl ilə düzəltməli olacaqsınız.'; $lang['i_policy'] = 'İlkin giriş haqları siyasəti'; $lang['i_pol0'] = 'Tam açıq wiki (oxumaq, yazmaq, fayl yükləmək hamıya olar)'; $lang['i_pol1'] = 'Acıq wiki (oxumaq hamıya olar, yazmaq və fayl yükləmək ancaq üzv olan istifadəçilərə olar)'; diff --git a/inc/lang/bg/lang.php b/inc/lang/bg/lang.php index 562dc78b3..7a9a20469 100644 --- a/inc/lang/bg/lang.php +++ b/inc/lang/bg/lang.php @@ -298,10 +298,10 @@ $lang['i_confexists'] = '%s вече съществува' $lang['i_writeerr'] = '%s не можа да бъде създаден. Трябва да проверите правата за достъп до директорията/файла и да създадете файла ръчно.'; $lang['i_badhash'] = 'Файлът dokuwiki.php не може да бъде разпознат или е променен (hash=%s)'; $lang['i_badval'] = '%s - непозволена или празна стойност'; -$lang['i_success'] = 'Настройването приключи успешно. Вече можете да изтриете файла install.php. Продължете към Вашето ново DokuWiki.'; +$lang['i_success'] = 'Настройването приключи успешно. Вече можете да изтриете файла install.php. Продължете към Вашето ново DokuWiki.'; $lang['i_failure'] = 'Възникнаха грешки при записването на файловете с настройки. Вероятно ще се наложи да ги поправите ръчно, - за да можете да ползвате Вашето ново DokuWiki.'; + за да можете да ползвате Вашето ново DokuWiki.'; $lang['i_policy'] = 'Първоначална политика за достъп'; $lang['i_pol0'] = 'Отворено Wiki (всеки може да чете, пише и качва)'; $lang['i_pol1'] = 'Публично Wiki (всеки може да чете, само регистрирани пишат и качват)'; diff --git a/inc/lang/ca-valencia/lang.php b/inc/lang/ca-valencia/lang.php index e299f6427..e62010418 100644 --- a/inc/lang/ca-valencia/lang.php +++ b/inc/lang/ca-valencia/lang.php @@ -211,9 +211,9 @@ $lang['i_writeerr'] = 'No es pot crear %s. Haurà de com $lang['i_badhash'] = 'dokuwiki.php substituït o modificat (hash=%s)'; $lang['i_badval'] = '%s - valor illegal o buit'; $lang['i_success'] = 'La configuració ha finalisat correctament. Ya pot borrar l\'archiu install.php. Passe al -nou DokuWiki.'; +nou DokuWiki.'; $lang['i_failure'] = 'Han aparegut alguns erros escrivint els archius de configuració. Deurà arreglar-los manualment abans de que -puga utilisar el nou DokuWiki.'; +puga utilisar el nou DokuWiki.'; $lang['i_policy'] = 'Política inicial ACL'; $lang['i_pol0'] = 'Wiki obert (llegir, escriure i enviar tots)'; $lang['i_pol1'] = 'Wiki públic (llegir tots, escriure i enviar només usuaris registrats)'; diff --git a/inc/lang/ca/lang.php b/inc/lang/ca/lang.php index 3a1412617..7fc078419 100644 --- a/inc/lang/ca/lang.php +++ b/inc/lang/ca/lang.php @@ -208,8 +208,8 @@ $lang['i_confexists'] = '%s ja existeix'; $lang['i_writeerr'] = 'No es pot crear %s. Comproveu els permisos del directori i/o del fitxer i creeu el fitxer manualment.'; $lang['i_badhash'] = 'dokuwiki.php no reconegut o modificat (hash=%s)'; $lang['i_badval'] = '%s - valor il·legal o buit'; -$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; -$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; +$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; +$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; $lang['i_policy'] = 'Política ACL inicial'; $lang['i_pol0'] = 'Wiki obert (tothom pot llegir, escriure i penjar fitxers)'; $lang['i_pol1'] = 'Wiki públic (tothom pot llegir, els usuaris registrats poden escriure i penjar fitxers)'; diff --git a/inc/lang/cs/lang.php b/inc/lang/cs/lang.php index 608beafd8..11033ff68 100644 --- a/inc/lang/cs/lang.php +++ b/inc/lang/cs/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s již existuje'; $lang['i_writeerr'] = 'Nelze vytvořit %s. Budete muset zkontrolovat práva k souborům či adresářům a vytvořit tento soubor ručně.'; $lang['i_badhash'] = 'soubor dokuwiki.php (hash=%s) nebyl rozpoznán nebo byl upraven'; $lang['i_badval'] = '%s - neplatná nebo prázdná hodnota'; -$lang['i_success'] = 'Konfigurace byla úspěšně dokončena. Nyní můžete smazat soubor install.php. Pokračujte do své nové DokuWiki.'; -$lang['i_failure'] = 'Vyskytly se nějaké chyby při zápisu do konfiguračních souborů. Budete je nejspíš muset upravit ručně před použitím své nové DokuWiki.'; +$lang['i_success'] = 'Konfigurace byla úspěšně dokončena. Nyní můžete smazat soubor install.php. Pokračujte do své nové DokuWiki.'; +$lang['i_failure'] = 'Vyskytly se nějaké chyby při zápisu do konfiguračních souborů. Budete je nejspíš muset upravit ručně před použitím své nové DokuWiki.'; $lang['i_policy'] = 'Úvodní politika ACL'; $lang['i_pol0'] = 'Otevřená wiki (čtení, zápis a upload pro všechny)'; $lang['i_pol1'] = 'Veřejná wiki (čtení pro všechny, zápis a upload pro registrované uživatele)'; diff --git a/inc/lang/da/lang.php b/inc/lang/da/lang.php index 97f2138b4..2c26cd3da 100644 --- a/inc/lang/da/lang.php +++ b/inc/lang/da/lang.php @@ -285,8 +285,8 @@ $lang['i_confexists'] = '%s eksisterer allerede'; $lang['i_writeerr'] = 'Kunne ikke oprette %s. Du bliver nød til at tjekke mappe/fil- tilladelserne og oprette filen manuelt.'; $lang['i_badhash'] = 'uigenkendelig eller modificeret dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - ulovlig eller tom værdi'; -$lang['i_success'] = 'Konfigurationen fulførtedes med success. Du kan nu slette install.php filen. Fortsætte til din nye DokuWiki.'; -$lang['i_failure'] = 'Nogle fejl forekom mens konfigurations filerne skulle skrives. Du er mulighvis nød til at fixe dem manuelt før du kan bruge din nye DokuWiki.'; +$lang['i_success'] = 'Konfigurationen fulførtedes med success. Du kan nu slette install.php filen. Fortsætte til din nye DokuWiki.'; +$lang['i_failure'] = 'Nogle fejl forekom mens konfigurations filerne skulle skrives. Du er mulighvis nød til at fixe dem manuelt før du kan bruge din nye DokuWiki.'; $lang['i_policy'] = 'Begyndende ACL politik'; $lang['i_pol0'] = 'Åben Wiki (alle kan læse, skrive og uploade)'; $lang['i_pol1'] = 'Offentlig Wiki (alle kan læse, kun registrerede brugere kan skrive og overføre)'; diff --git a/inc/lang/de-informal/lang.php b/inc/lang/de-informal/lang.php index 37469522f..cce898b15 100644 --- a/inc/lang/de-informal/lang.php +++ b/inc/lang/de-informal/lang.php @@ -287,8 +287,8 @@ $lang['i_confexists'] = '%s existiert bereits'; $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. Du solltest die Verzeichnis-/Datei-Rechte überprüfen und die Datei manuell anlegen.'; $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; -$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Du kannst jetzt die install.php löschen. Dein neues DokuWiki ist jetzt für dich bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese vermutlich von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; +$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Du kannst jetzt die install.php löschen. Dein neues DokuWiki ist jetzt für dich bereit.'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese vermutlich von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; $lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; $lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; $lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php index 4ea75157b..07052fb3d 100644 --- a/inc/lang/de/lang.php +++ b/inc/lang/de/lang.php @@ -289,8 +289,8 @@ $lang['i_confexists'] = '%s existiert bereits'; $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. Sie sollten die Verzeichnis-/Datei-Rechte überprüfen und die Datei manuell anlegen.'; $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; -$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Sie können jetzt die install.php löschen. Ihr neues DokuWiki ist jetzt für Sie bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese vermutlich von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; +$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Sie können jetzt die install.php löschen. Ihr neues DokuWiki ist jetzt für Sie bereit.'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese vermutlich von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; $lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; $lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; $lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; diff --git a/inc/lang/el/lang.php b/inc/lang/el/lang.php index 95f1a1ea5..d5bde6929 100644 --- a/inc/lang/el/lang.php +++ b/inc/lang/el/lang.php @@ -270,8 +270,8 @@ $lang['i_confexists'] = '%s υπάρχει ήδη'; $lang['i_writeerr'] = 'Δεν είναι δυνατή η δημιουργία του %s. Πρέπει να διορθώσετε τα δικαιώματα πρόσβασης αυτού του φακέλου/αρχείου και να δημιουργήσετε το αρχείο χειροκίνητα!'; $lang['i_badhash'] = 'Μη αναγνωρίσιμο ή τροποποιημένο αρχείο dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - λάθος ή ανύπαρκτη τιμή'; -$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; -$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; +$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; +$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; $lang['i_policy'] = 'Αρχική πολιτική Λίστας Δικαιωμάτων Πρόσβασης - ACL'; $lang['i_pol0'] = 'Ανοιχτό Wiki (όλοι μπορούν να διαβάσουν ή να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; $lang['i_pol1'] = 'Δημόσιο Wiki (όλοι μπορούν να διαβάσουν σελίδες αλλά μόνο οι εγγεγραμμένοι χρήστες μπορούν να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 3c51ba224..73c3522fd 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -302,9 +302,9 @@ $lang['i_writeerr'] = 'Unable to create %s. You will nee $lang['i_badhash'] = 'unrecognised or modified dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - illegal or empty value'; $lang['i_success'] = 'The configuration was finished successfully. You may delete the install.php file now. Continue to - your new DokuWiki.'; + your new DokuWiki.'; $lang['i_failure'] = 'Some errors occurred while writing the configuration files. You may need to fix them manually before - you can use your new DokuWiki.'; + you can use your new DokuWiki.'; $lang['i_policy'] = 'Initial ACL policy'; $lang['i_pol0'] = 'Open Wiki (read, write, upload for everyone)'; $lang['i_pol1'] = 'Public Wiki (read for everyone, write and upload for registered users)'; diff --git a/inc/lang/eo/lang.php b/inc/lang/eo/lang.php index b2c64b2a6..3e0d86386 100644 --- a/inc/lang/eo/lang.php +++ b/inc/lang/eo/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s jam ekzistas'; $lang['i_writeerr'] = 'Ne eblas krei "%s". Vi bezonas kontroli la permesojn de la dosier(uj)oj kaj mem krej la dosieron.'; $lang['i_badhash'] = 'dokuwiki.php ne estas rekonebla aŭ ĝi estas modifita (hash=%s)'; $lang['i_badval'] = '%s - malvalida aŭ malplena valoro'; -$lang['i_success'] = 'La agordado sukcese kompletiĝis. Vi povas forigi la dosieron nun. Pluiru al via nova DokuWiki.'; -$lang['i_failure'] = 'Kelkaj eraroj okazis dum la konservo de la agordaj dosieroj. Vi devas senpere korekti ilin antaŭ ol vi povos uzi vian novan DokuWiki-on. '; +$lang['i_success'] = 'La agordado sukcese kompletiĝis. Vi povas forigi la dosieron nun. Pluiru al via nova DokuWiki.'; +$lang['i_failure'] = 'Kelkaj eraroj okazis dum la konservo de la agordaj dosieroj. Vi devas senpere korekti ilin antaŭ ol vi povos uzi vian novan DokuWiki-on. '; $lang['i_policy'] = 'Komenca ACL-a agordo'; $lang['i_pol0'] = 'Malferma Vikio (legi, skribi, alŝuti povas ĉiuj)'; $lang['i_pol1'] = 'Publika Vikio (legi povas ĉiuj, skribi kaj alŝuti povas registritaj uzantoj)'; diff --git a/inc/lang/es/lang.php b/inc/lang/es/lang.php index ee3da258b..c09b12121 100644 --- a/inc/lang/es/lang.php +++ b/inc/lang/es/lang.php @@ -287,8 +287,8 @@ $lang['i_confexists'] = '%s ya existe'; $lang['i_writeerr'] = 'Imposible crear %s. Se necesita que usted controle los permisos del fichero/directorio y que cree el fichero manualmente.'; $lang['i_badhash'] = 'dokuwiki.php no reconocido o modificado (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal o vacío'; -$lang['i_success'] = 'La configuración ha concluido correctamente. Ahora puede eliminar el archivo install.php. Visite su nuevo DokuWiki.'; -$lang['i_failure'] = 'Han ocurrido algunos errores durante la escritura de los ficheros de configuración. Puede ser que necesite corregirlos manualmente antes de poder usar su nuevo DokuWiki.'; +$lang['i_success'] = 'La configuración ha concluido correctamente. Ahora puede eliminar el archivo install.php. Visite su nuevo DokuWiki.'; +$lang['i_failure'] = 'Han ocurrido algunos errores durante la escritura de los ficheros de configuración. Puede ser que necesite corregirlos manualmente antes de poder usar su nuevo DokuWiki.'; $lang['i_policy'] = 'Política de ACL inicial'; $lang['i_pol0'] = 'Wiki abierto (leer, escribir y subir archivos para todos)'; $lang['i_pol1'] = 'Wiki público (leer para todos, escribir y subir archivos para usuarios registrados únicamente)'; diff --git a/inc/lang/et/lang.php b/inc/lang/et/lang.php index 5e5caa1c8..37d4538ec 100644 --- a/inc/lang/et/lang.php +++ b/inc/lang/et/lang.php @@ -223,8 +223,8 @@ $lang['i_permfail'] = 'Dokuwiki ei saa kirjutada faili %svotre nouveau DokuWiki.'; -$lang['i_failure'] = 'Des erreurs sont survenues lors de l\'écriture des fichiers de configuration. Il vous faudra les corriger manuellement avant de pouvoir utiliser votre nouveau DokuWiki.'; +$lang['i_success'] = 'L\'installation s\'est terminée avec succès. Vous pouvez maintenant supprimer le fichier « install.php ». Continuer avec votre nouveau DokuWiki.'; +$lang['i_failure'] = 'Des erreurs sont survenues lors de l\'écriture des fichiers de configuration. Il vous faudra les corriger manuellement avant de pouvoir utiliser votre nouveau DokuWiki.'; $lang['i_policy'] = 'Politique d\'ACL initiale'; $lang['i_pol0'] = 'Wiki ouvert (lecture, écriture, envoi de fichiers pour tout le monde)'; $lang['i_pol1'] = 'Wiki public (lecture pour tout le monde, écriture et envoi de fichiers pour les utilisateurs enregistrés)'; diff --git a/inc/lang/gl/lang.php b/inc/lang/gl/lang.php index 9636d6c15..466f0fb8c 100644 --- a/inc/lang/gl/lang.php +++ b/inc/lang/gl/lang.php @@ -269,9 +269,9 @@ $lang['i_writeerr'] = 'Non se puido crear %s. Terás de $lang['i_badhash'] = 'dokuwiki.php irrecoñecíbel ou modificado (hash=%s)'; $lang['i_badval'] = '%s - ilegal ou valor baleiro'; $lang['i_success'] = 'A configuración rematou correctamente. Agora podes eliminar o arquivo install.php. Continúa deica o - teu novo DokuWiki.'; + teu novo DokuWiki.'; $lang['i_failure'] = 'Houbo algúns erros ao tentar escribir os arquivos de configuración. Pode que precises solucionalos de xeito manual antes - de poderes empregar o teu novo DokuWiki.'; + de poderes empregar o teu novo DokuWiki.'; $lang['i_policy'] = 'Regras iniciais da ACL'; $lang['i_pol0'] = 'Wiki Aberto (lectura, escritura, subida de arquivos para todas as persoas)'; $lang['i_pol1'] = 'Wiki Público (lectura para todas as persoas, escritura e subida de arquivos para usuarios rexistrados)'; diff --git a/inc/lang/he/lang.php b/inc/lang/he/lang.php index 00eb4549b..9cbcc3c4f 100644 --- a/inc/lang/he/lang.php +++ b/inc/lang/he/lang.php @@ -249,8 +249,8 @@ $lang['i_confexists'] = '%s כבר קיים'; $lang['i_writeerr'] = 'אין אפשרות ליצור את %s. נא לבדוק את הרשאות הקובץ/תיקייה וליצור את הקובץ ידנית.'; $lang['i_badhash'] = 'הקובץ Dokuwiki.php אינו מזוהה או שעבר שינויים (hash=%s)'; $lang['i_badval'] = '%s - הערך אינו חוקי או ריק'; -$lang['i_success'] = 'תהליך ההגדרה הסתיים בהצלחה. כעת ניתן למחוק את הקובץ install.php ולהמשיך אל ה־DokuWiki החדש שלך.'; -$lang['i_failure'] = 'מספר שגיאות אירעו בעת כתיבת קובצי התצורה. יתכן כי יהיה צורך לתקנם ידנית לפני שניתן יהיה להשתמש ב־DokuWiki החדש שלך.'; +$lang['i_success'] = 'תהליך ההגדרה הסתיים בהצלחה. כעת ניתן למחוק את הקובץ install.php ולהמשיך אל ה־DokuWiki החדש שלך.'; +$lang['i_failure'] = 'מספר שגיאות אירעו בעת כתיבת קובצי התצורה. יתכן כי יהיה צורך לתקנם ידנית לפני שניתן יהיה להשתמש ב־DokuWiki החדש שלך.'; $lang['i_policy'] = 'מדיניות ACL התחלתית'; $lang['i_pol0'] = 'ויקי פתוח (קריאה, כתיבה והעלאה לכולם)'; $lang['i_pol1'] = ' ויקי ציבורי (קריאה לכולם, כתיבה והעלאה למשתמשים רשומים)'; diff --git a/inc/lang/hr/lang.php b/inc/lang/hr/lang.php index a0438d915..ac2a740ef 100644 --- a/inc/lang/hr/lang.php +++ b/inc/lang/hr/lang.php @@ -247,8 +247,8 @@ $lang['i_confexists'] = '%s već postoji'; $lang['i_writeerr'] = 'Ne može se kreirati %s. Trebate provjeriti dozvole direktorija/datoteke i kreirati dokument ručno.'; $lang['i_badhash'] = 'neprepoznat ili promijenjen dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - nedozvoljena ili prazna vrijednost'; -$lang['i_success'] = 'Konfiguracija je uspješno završena. Sada možete obrisati install.php datoteku. Nastavite na vaš novi DokuWiki.'; -$lang['i_failure'] = 'Pojavile su se neke greške prilikom pisanja konfiguracijskih datoteka. Morati ćete ih ručno ispraviti da bi mogli koristiti vaš novi DokuWiki.'; +$lang['i_success'] = 'Konfiguracija je uspješno završena. Sada možete obrisati install.php datoteku. Nastavite na vaš novi DokuWiki.'; +$lang['i_failure'] = 'Pojavile su se neke greške prilikom pisanja konfiguracijskih datoteka. Morati ćete ih ručno ispraviti da bi mogli koristiti vaš novi DokuWiki.'; $lang['i_policy'] = 'Inicijalna ACL politika'; $lang['i_pol0'] = 'Otvoreni Wiki (čitanje, pisanje, učitavanje za sve)'; $lang['i_pol1'] = 'Javni Wiki (čitanje za sve, pisanje i učitavanje za registrirane korisnike)'; diff --git a/inc/lang/hu/lang.php b/inc/lang/hu/lang.php index 671b225f2..43b79b41b 100644 --- a/inc/lang/hu/lang.php +++ b/inc/lang/hu/lang.php @@ -252,8 +252,8 @@ $lang['i_confexists'] = '%s már létezik.'; $lang['i_writeerr'] = 'Nem tudom ezt létrehozni: %s. Ellenőrizd a könyvtár/fájl jogosultságokat, és hozd létre az állományt kézzel.'; $lang['i_badhash'] = 'A dokuwiki.php nem felismerhető vagy módosított (hash=%s)'; $lang['i_badval'] = '%s - nem helyes vagy üres érték'; -$lang['i_success'] = 'A beállítás sikeresen befejeződött. Most már letörölhető az install.php fájl. Látogasd meg az új DokuWikidet!'; -$lang['i_failure'] = 'Hiba lépett fel a konfigurációs állományok írásakor. Ki kell javítanod kézzel, mielőtt használni kezded az új DokuWikidet.'; +$lang['i_success'] = 'A beállítás sikeresen befejeződött. Most már letörölhető az install.php fájl. Látogasd meg az új DokuWikidet!'; +$lang['i_failure'] = 'Hiba lépett fel a konfigurációs állományok írásakor. Ki kell javítanod kézzel, mielőtt használni kezded az új DokuWikidet.'; $lang['i_policy'] = 'Kezdeti hozzáférési politika'; $lang['i_pol0'] = 'Nyitott Wiki (mindenki olvashatja, írhatja, és fájlokat tölthet fel)'; $lang['i_pol1'] = 'Publikus Wiki (mindenki olvashatja, de csak regisztrált felhasználók írhatják, és tölthetnek fel fájlokat)'; diff --git a/inc/lang/ia/lang.php b/inc/lang/ia/lang.php index d7be1eff3..aff133698 100644 --- a/inc/lang/ia/lang.php +++ b/inc/lang/ia/lang.php @@ -247,9 +247,9 @@ $lang['i_writeerr'] = 'Impossibile crear %s. Tu debe ver $lang['i_badhash'] = 'dokuwiki.php non recognoscite o modificate (hash=%s)'; $lang['i_badval'] = '%s - valor vacue o invalide'; $lang['i_success'] = 'Le configuration ha succedite. Tu pote ora deler le file install.php. Continua a -tu nove DokuWiki.'; +tu nove DokuWiki.'; $lang['i_failure'] = 'Alcun errores occurreva durante le scriptura del files de configuration. Es possibile que tu debe remediar iste errores manualmente ante que -tu pote usar tu nove DokuWiki.'; +tu pote usar tu nove DokuWiki.'; $lang['i_policy'] = 'Politica de ACL interne'; $lang['i_pol0'] = 'Wiki aperte (lectura, scriptura, incargamento pro omnes)'; $lang['i_pol1'] = 'Wiki public (lectura pro omnes, scriptura e incargamento pro usatores registrate)'; diff --git a/inc/lang/id/lang.php b/inc/lang/id/lang.php index 2302d4f6f..566e8dcb0 100644 --- a/inc/lang/id/lang.php +++ b/inc/lang/id/lang.php @@ -188,8 +188,8 @@ $lang['i_confexists'] = '%s sudah ada'; $lang['i_writeerr'] = 'Tidak dapat membuat %s. Anda harus memeriksa konfigurasi hak akses direktori/berkas dan membuatnya secara manual.'; $lang['i_badhash'] = 'dokuwiki.php tidak dikenal atau sudah diubah (hash=%s)'; $lang['i_badval'] = '%s - tidak valid atau belum diisi'; -$lang['i_success'] = 'Konfigurasi telah berhasil. Anda boleh menghapus berkas install.php sekarang. Lanjutkan ke DokuWiki baru Anda.'; -$lang['i_failure'] = 'Terdapat beberapa kesalahan dalam menulis berkas konfigurasi. Anda harus memperbaikinnya sendiri sebelum dapat menggunakan DokuWiki baru Anda.'; +$lang['i_success'] = 'Konfigurasi telah berhasil. Anda boleh menghapus berkas install.php sekarang. Lanjutkan ke DokuWiki baru Anda.'; +$lang['i_failure'] = 'Terdapat beberapa kesalahan dalam menulis berkas konfigurasi. Anda harus memperbaikinnya sendiri sebelum dapat menggunakan DokuWiki baru Anda.'; $lang['i_policy'] = 'Policy ACL awal'; $lang['i_pol0'] = 'Wiki Terbuka (baca, tulis, upload untuk semua orang)'; $lang['i_pol1'] = 'Wiki Publik (baca untuk semua orang, tulis dan upload untuk pengguna terdaftar)'; diff --git a/inc/lang/it/lang.php b/inc/lang/it/lang.php index 70082caae..6ae864f8b 100644 --- a/inc/lang/it/lang.php +++ b/inc/lang/it/lang.php @@ -281,8 +281,8 @@ $lang['i_confexists'] = '%s esiste già'; $lang['i_writeerr'] = 'Impossibile creare %s. E\' necessario verificare i permessi della directory o del file oppure creare il file manualmente.'; $lang['i_badhash'] = 'dokuwiki.php (hash=%s) non riconosciuto o modificato'; $lang['i_badval'] = '%s - valore vuoto o non valido'; -$lang['i_success'] = 'La configurazione è stata completata correttamente. Ora è possibile eliminare il file install.php. Poi, visita il tuo nuovo DokuWiki.'; -$lang['i_failure'] = 'Si sono verificati errori durante la scrittura dei file di configurazione. Potrebbe essere necessario correggerli manualmente prima di poter utilizzare il tuo nuovo DokuWiki.'; +$lang['i_success'] = 'La configurazione è stata completata correttamente. Ora è possibile eliminare il file install.php. Poi, visita il tuo nuovo DokuWiki.'; +$lang['i_failure'] = 'Si sono verificati errori durante la scrittura dei file di configurazione. Potrebbe essere necessario correggerli manualmente prima di poter utilizzare il tuo nuovo DokuWiki.'; $lang['i_policy'] = 'Regole di accesso iniziali'; $lang['i_pol0'] = 'Wiki Aperto (lettura, scrittura, caricamento file per tutti)'; $lang['i_pol1'] = 'Wiki Pubblico (lettura per tutti, scrittura e caricamento file per gli utenti registrati)'; diff --git a/inc/lang/ja/lang.php b/inc/lang/ja/lang.php index 490c84cc9..93300e20e 100644 --- a/inc/lang/ja/lang.php +++ b/inc/lang/ja/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s は既に存在します'; $lang['i_writeerr'] = '%s を作成できません。ディレクトリとファイルの権限を確認し、それらを手動で作成する必要があります。'; $lang['i_badhash'] = 'dokuwiki.php が認識できないか、編集されています(hash=%s)'; $lang['i_badval'] = '%s - 正しくない、もしくは値が空です'; -$lang['i_success'] = '設定ファイルは正しく作成されました。作成した DokuWikiを使用するには install.php を削除してください。'; -$lang['i_failure'] = '設定ファイルの作成中にエラーが発生しました。作成した DokuWikiを使用する前に、それらの問題を手動で修正する必要があります。'; +$lang['i_success'] = '設定ファイルは正しく作成されました。作成した DokuWikiを使用するには install.php を削除してください。'; +$lang['i_failure'] = '設定ファイルの作成中にエラーが発生しました。作成した DokuWikiを使用する前に、それらの問題を手動で修正する必要があります。'; $lang['i_policy'] = 'ACL初期設定'; $lang['i_pol0'] = 'オープン Wiki(全ての人に、閲覧・書き込み・アップロードを許可)'; $lang['i_pol1'] = 'パブリック Wiki(閲覧は全ての人が可能、書き込み・アップロードは登録ユーザーのみ)'; diff --git a/inc/lang/ko/lang.php b/inc/lang/ko/lang.php index 7b4e30a49..1a618f1a7 100644 --- a/inc/lang/ko/lang.php +++ b/inc/lang/ko/lang.php @@ -279,8 +279,8 @@ $lang['i_confexists'] = '%s(은)는 이미 존재합니다 $lang['i_writeerr'] = '%s(을)를 만들 수 없습니다. 먼저 디렉토리/파일 권한을 확인하고 파일을 수동으로 만들기 바랍니다.'; $lang['i_badhash'] = 'dokuwiki.php를 인식할 수 없거나 원본 파일이 아닙니다. (해시=%s)'; $lang['i_badval'] = '%s - 유효하지 않거나 빈 값입니다.'; -$lang['i_success'] = '환경 설정이 성공적으로 끝났습니다. 지금 install.php를 지워도 상관없습니다. 새 DokuWiki로 들어갑니다.'; -$lang['i_failure'] = '환경 설정 파일에 쓰는 도중에 오류가 발생했습니다. 새 DokuWiki를 사용하기 전에 수동으로 문제를 해결할 필요가 있습니다.'; +$lang['i_success'] = '환경 설정이 성공적으로 끝났습니다. 지금 install.php를 지워도 상관없습니다. 새 DokuWiki로 들어갑니다.'; +$lang['i_failure'] = '환경 설정 파일에 쓰는 도중에 오류가 발생했습니다. 새 DokuWiki를 사용하기 전에 수동으로 문제를 해결할 필요가 있습니다.'; $lang['i_policy'] = '초기 ACL 정책'; $lang['i_pol0'] = '열린 위키 (누구나 읽기, 쓰기, 올리기가 가능합니다.)'; $lang['i_pol1'] = '공개 위키 (누구나 읽을 수 있지만, 등록된 사용자만 쓰기와 올리기가 가능합니다.)'; diff --git a/inc/lang/la/lang.php b/inc/lang/la/lang.php index 3970f453a..c7d654e96 100644 --- a/inc/lang/la/lang.php +++ b/inc/lang/la/lang.php @@ -246,8 +246,8 @@ $lang['i_confexists'] = '%s iam est.'; $lang['i_writeerr'] = '%s non creari potest. Manu illum creas.'; $lang['i_badhash'] = 'Ignotum uel mutatum dokuwiki.php (%s)'; $lang['i_badval'] = '%s non legitimum uel uacuom'; -$lang['i_success'] = 'Administratio feliciter perficitur. Delere install.php documentum potes. I ad hanc paginam ut continues.'; -$lang['i_failure'] = 'Aliqui errores dum documenta administrantur sunt. Manu onerare omnes potes priusquam tuo nouo uice uteris.'; +$lang['i_success'] = 'Administratio feliciter perficitur. Delere install.php documentum potes. I ad hanc paginam ut continues.'; +$lang['i_failure'] = 'Aliqui errores dum documenta administrantur sunt. Manu onerare omnes potes priusquam tuo nouo uice uteris.'; $lang['i_policy'] = 'ICA ratio prima'; $lang['i_pol0'] = 'Vicem aperire (omnes legere, scribere, onerare possunt)'; $lang['i_pol1'] = 'Publicus uicis (omnes legere, Sodales scribere et onerare possunt)'; diff --git a/inc/lang/lv/lang.php b/inc/lang/lv/lang.php index b02e7f471..67480abfc 100644 --- a/inc/lang/lv/lang.php +++ b/inc/lang/lv/lang.php @@ -264,8 +264,8 @@ $lang['i_confexists'] = '%s jau ir'; $lang['i_writeerr'] = 'Nevar izveidot %s. Jāpārbauda direktorijas/faila tiesības un fails jāizveido pašam.'; $lang['i_badhash'] = 'nepazīstams vai izmainīts dokuwiki.php fails (hash=%s)'; $lang['i_badval'] = '%s - neatļauta vai tukša vērtība'; -$lang['i_success'] = 'Konfigurēšana veiksmīgi pabeigta. Tagad vari nodzēst failu install.php. Tālāk turpini savā jaunajā DokuWiki.'; -$lang['i_failure'] = 'Rakstot konfigurācijas failu, gadījās dažas kļūmes. Pirms lieto savu jauno DokuWiki, tās varbūt jāizlabo.'; +$lang['i_success'] = 'Konfigurēšana veiksmīgi pabeigta. Tagad vari nodzēst failu install.php. Tālāk turpini savā jaunajā DokuWiki.'; +$lang['i_failure'] = 'Rakstot konfigurācijas failu, gadījās dažas kļūmes. Pirms lieto savu jauno DokuWiki, tās varbūt jāizlabo.'; $lang['i_policy'] = 'Sākotnējā ACL politika'; $lang['i_pol0'] = 'Atvērts Wiki (raksta, lasa un augšupielādē ikviens)'; $lang['i_pol1'] = 'Publisks Wiki (lasa ikviens, raksta un augšupielādē reģistrēti lietotāji)'; diff --git a/inc/lang/mk/lang.php b/inc/lang/mk/lang.php index 62400063c..f04c51e37 100644 --- a/inc/lang/mk/lang.php +++ b/inc/lang/mk/lang.php @@ -214,8 +214,8 @@ $lang['i_confexists'] = '%s веќе постои'; $lang['i_writeerr'] = 'Не може да се креира %s. Треба да ги проверите пермисиите на директориумот/датотеката и рачно да ја креирате датотеката.'; $lang['i_badhash'] = 'непозната или изменете dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - нелегална или празна вредност'; -$lang['i_success'] = 'Конфигурацијата успешно заврши. Сега можете да ја избришете датотеката install.php. Продолжете до вашето ново DokuWiki.'; -$lang['i_failure'] = 'Се појавија некои грешки при запишувањето на конфигурациските датотеки. Можеби треба да ги поравите рачно пред да можете да го користите вашето ново DokuWiki.'; +$lang['i_success'] = 'Конфигурацијата успешно заврши. Сега можете да ја избришете датотеката install.php. Продолжете до вашето ново DokuWiki.'; +$lang['i_failure'] = 'Се појавија некои грешки при запишувањето на конфигурациските датотеки. Можеби треба да ги поравите рачно пред да можете да го користите вашето ново DokuWiki.'; $lang['i_policy'] = 'Почетна ACL политика'; $lang['i_pol0'] = 'Отвори вики (читај, запиши, качи за сите)'; $lang['i_pol1'] = 'Јавно вики (читај за сите, запиши и качи за регистрирани корисници)'; diff --git a/inc/lang/mr/lang.php b/inc/lang/mr/lang.php index 32781e6d4..c6064a610 100644 --- a/inc/lang/mr/lang.php +++ b/inc/lang/mr/lang.php @@ -258,8 +258,8 @@ $lang['i_confexists'] = '%s आधीच अस्ति $lang['i_writeerr'] = '%s निर्माण करू शकलो नाही. तुम्हाला डिरेक्टरी / फाइल च्या परवानग्या तपासून स्वतःच ही फाइल बनवावी लागेल.'; $lang['i_badhash'] = 'अनाकलनीय किंवा बदललेले dokuwiki.php (hash=%s)'; $lang['i_badval'] = 'code>%s - अवैध किंवा रिकामा मजकूर.'; -$lang['i_success'] = 'व्यवस्था लावण्याचे काम यशस्वीरीत्या पार पडले. आता तुम्ही install.php डिलीट करू शकता. तुमच्या नविन डॉक्युविकि वर जा.'; -$lang['i_failure'] = 'कॉन्फिगुरेशनच्या फाइल सुरक्षित करताना काही अडचणी आल्या आहेत. तुमची नवीन डॉक्युविकि वापरण्याआधी तुम्हाला ह्या फाइल स्वतः ठीक कराव्या लागतील.'; +$lang['i_success'] = 'व्यवस्था लावण्याचे काम यशस्वीरीत्या पार पडले. आता तुम्ही install.php डिलीट करू शकता. तुमच्या नविन डॉक्युविकि वर जा.'; +$lang['i_failure'] = 'कॉन्फिगुरेशनच्या फाइल सुरक्षित करताना काही अडचणी आल्या आहेत. तुमची नवीन डॉक्युविकि वापरण्याआधी तुम्हाला ह्या फाइल स्वतः ठीक कराव्या लागतील.'; $lang['i_policy'] = 'आरंभीची ACL पॉलिसी'; $lang['i_pol0'] = 'मुक्त विकी ( सर्वांना वाचन, लेखन व अपलोड करण्याची परवानगी )'; $lang['i_pol1'] = 'सार्वजनिक विकी ( सर्वांना वाचण्याची मुभा , लेखन व अपलोडची परवानगी फक्त नोंदणीकृत सदस्यांना )'; diff --git a/inc/lang/ne/lang.php b/inc/lang/ne/lang.php index 82ca389f7..bde40a670 100644 --- a/inc/lang/ne/lang.php +++ b/inc/lang/ne/lang.php @@ -191,8 +191,8 @@ $lang['i_confexists'] = '%s पहिले देखि $lang['i_writeerr'] = '%s बनाउन असमर्थ । तपाईले डाइरेक्टरी / फाइल अनुमति जाच्नु पर्छ र फाइल आफैले बनाउनु पर्छ ।'; $lang['i_badhash'] = 'पहिचान हुन नसकेको वा परिवर्तित okuwiki.php (hash=code>%s)'; $lang['i_badval'] = '%s - अवैध वा रित्तो मान '; -$lang['i_success'] = 'स्थापना सफलरुपमा समाप्त भयो ।तपाई install.php मेट्न सक्नुहु्न्छ । तपाईको नयाँ DokuWiki निरन्तर गर्न सक्नुहुन्छ ।'; -$lang['i_failure'] = 'स्थापना समयमा केहि त्रुटि फेला पर्यो ।तपाईले आफैले यसलाई तपाईको नयाँ DokuWiki प्रयोग गर्नु अगि सच्याउनुपर्ने हुन्छ ।'; +$lang['i_success'] = 'स्थापना सफलरुपमा समाप्त भयो ।तपाई install.php मेट्न सक्नुहु्न्छ । तपाईको नयाँ DokuWiki निरन्तर गर्न सक्नुहुन्छ ।'; +$lang['i_failure'] = 'स्थापना समयमा केहि त्रुटि फेला पर्यो ।तपाईले आफैले यसलाई तपाईको नयाँ DokuWiki प्रयोग गर्नु अगि सच्याउनुपर्ने हुन्छ ।'; $lang['i_policy'] = 'सुरुको ACL निति'; $lang['i_pol0'] = 'खुल्ला विकि (पठन, लेखन , अपलोड ) सबैका लागि'; $lang['i_pol1'] = 'Public विकि (पठन सवैका लागि,लेखन र अपलोड दर्ता गरिएका प्रयपगकर्ताका लागि ) '; diff --git a/inc/lang/nl/lang.php b/inc/lang/nl/lang.php index 911ffdc10..6d14a0e4b 100644 --- a/inc/lang/nl/lang.php +++ b/inc/lang/nl/lang.php @@ -286,8 +286,8 @@ $lang['i_confexists'] = '%s bestaat reeds'; $lang['i_writeerr'] = 'Niet mogelijk om %s aan te maken. Controleer de directory/bestandspermissies en maak het bestand handmatig aan.'; $lang['i_badhash'] = 'Onbekende of aangepaste dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - onjuiste of lege waarde'; -$lang['i_success'] = 'De configuratie is succesvol afgerond. Je kunt nu het bestand install.php verwijderen. Ga naar je nieuwe DokuWiki.'; -$lang['i_failure'] = 'Fouten deden zich voor tijdens het schrijven naar de configuratiebestanden. Pas deze aan voor je gebruik kunt maken van je nieuwe DokuWiki.'; +$lang['i_success'] = 'De configuratie is succesvol afgerond. Je kunt nu het bestand install.php verwijderen. Ga naar je nieuwe DokuWiki.'; +$lang['i_failure'] = 'Fouten deden zich voor tijdens het schrijven naar de configuratiebestanden. Pas deze aan voor je gebruik kunt maken van je nieuwe DokuWiki.'; $lang['i_policy'] = 'Initieel ACL-beleid'; $lang['i_pol0'] = 'Open wiki (lezen, schrijven, uploaden voor iedereen)'; $lang['i_pol1'] = 'Publieke wiki (lezen voor iedereen, schrijven en uploaden voor geregistreerde gebruikers)'; diff --git a/inc/lang/no/lang.php b/inc/lang/no/lang.php index 97ef741ef..81aa39612 100644 --- a/inc/lang/no/lang.php +++ b/inc/lang/no/lang.php @@ -282,9 +282,9 @@ $lang['i_writeerr'] = 'Kunne ikke opprette %s. Du må sj $lang['i_badhash'] = 'ikke gjenkjent eller modifisert dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - ugyldig eller tom verdi'; $lang['i_success'] = 'Konfigurasjonen ble vellykket fullført. Du kan slette install.php filen nå. Fortsett til - din nye DokuWiki.'; + din nye DokuWiki.'; $lang['i_failure'] = 'En eller flere feil oppstod ved skriving til konfigurasjonsfilene. Du må kanskje fikse dem manuelt før - du kan bruke din nye DokuWiki.'; + du kan bruke din nye DokuWiki.'; $lang['i_policy'] = 'Innledende ACL-politikk'; $lang['i_pol0'] = 'Åpen Wiki (les, skriv og opplasting for alle)'; $lang['i_pol1'] = 'Offentlig Wiki (les for alle, skriving og opplasting bare for registrerte brukere)'; diff --git a/inc/lang/pl/lang.php b/inc/lang/pl/lang.php index 79d18bbf5..0f0381e8f 100644 --- a/inc/lang/pl/lang.php +++ b/inc/lang/pl/lang.php @@ -285,8 +285,8 @@ $lang['i_confexists'] = '%s już istnieje'; $lang['i_writeerr'] = 'Nie można utworzyć %s. Sprawdź uprawnienia do katalogu lub pliku i stwórz plik ręcznie.'; $lang['i_badhash'] = 'nierozpoznany lub zmodyfikowany plik dokuwiki.php (skrót=%s)'; $lang['i_badval'] = '%s - nieprawidłowa wartość lub jej brak'; -$lang['i_success'] = 'Konfiguracja pomyślnie zakończona. Możesz teraz usunąć plik install.php. Przejdź do Twojego nowego DokuWiki.'; -$lang['i_failure'] = 'Podczas zapisu plików konfiguracyjnych wystąpiły błędy. Musisz usunąć wszystkie problemy, zanim zaczniesz korzystać z Twojego nowego DokuWiki.'; +$lang['i_success'] = 'Konfiguracja pomyślnie zakończona. Możesz teraz usunąć plik install.php. Przejdź do Twojego nowego DokuWiki.'; +$lang['i_failure'] = 'Podczas zapisu plików konfiguracyjnych wystąpiły błędy. Musisz usunąć wszystkie problemy, zanim zaczniesz korzystać z Twojego nowego DokuWiki.'; $lang['i_policy'] = 'Wstępna polityka uprawnień ACL'; $lang['i_pol0'] = 'Otwarte Wiki (odczyt, zapis i dodawanie plików dla wszystkich)'; $lang['i_pol1'] = 'Publiczne Wiki (odczyt dla wszystkich, zapis i dodawanie plików tylko dla zarejestrowanych użytkowników)'; diff --git a/inc/lang/pt-br/lang.php b/inc/lang/pt-br/lang.php index d6dc8f96b..26a73bfae 100644 --- a/inc/lang/pt-br/lang.php +++ b/inc/lang/pt-br/lang.php @@ -261,8 +261,8 @@ $lang['i_confexists'] = '%s já existe'; $lang['i_writeerr'] = 'Não foi possível criar %s. É necessário checar as permissões de arquivos/diretórios e criar o arquivo manualmente.'; $lang['i_badhash'] = 'dokuwiki.php não reconhecido ou modificado (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal ou em branco'; -$lang['i_success'] = 'A configuração terminou com sucesso. Agora você deve excluir o arquivo install.php. Conheça o seu novo DokuWiki!'; -$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita dos arquivos de configuração. É necessário corrigi-los manualmente antes de usar seu novo DokuWiki'; +$lang['i_success'] = 'A configuração terminou com sucesso. Agora você deve excluir o arquivo install.php. Conheça o seu novo DokuWiki!'; +$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita dos arquivos de configuração. É necessário corrigi-los manualmente antes de usar seu novo DokuWiki'; $lang['i_policy'] = 'Política inicial de permissões'; $lang['i_pol0'] = 'Wiki aberto (leitura, escrita e envio de arquivos por todos)'; $lang['i_pol1'] = 'Wiki público (leitura por todos, escrita e envio de arquivos por usuários registrados)'; diff --git a/inc/lang/pt/lang.php b/inc/lang/pt/lang.php index a0b0f57e8..37b3fba8c 100644 --- a/inc/lang/pt/lang.php +++ b/inc/lang/pt/lang.php @@ -268,8 +268,8 @@ $lang['i_confexists'] = '%s já existe'; $lang['i_writeerr'] = 'Não foi possível criar %s. É preciso verificar as permissões e criar o ficheiro manualmente.'; $lang['i_badhash'] = 'dokuwiki.php não é o original ou não é reconhecido (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal ou vazio'; -$lang['i_success'] = 'A instalação e configuração inicial foram bem sucedidas. Pode remover o install.php. Aceda ao seu novo Wiki a correr o DokuWiki.'; -$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita nos ficheiros de configuração. Poderá ser preciso corrigi-los manualmente antes de poder aceder ao seu novo Wiki a correr o DokuWiki.'; +$lang['i_success'] = 'A instalação e configuração inicial foram bem sucedidas. Pode remover o install.php. Aceda ao seu novo Wiki a correr o DokuWiki.'; +$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita nos ficheiros de configuração. Poderá ser preciso corrigi-los manualmente antes de poder aceder ao seu novo Wiki a correr o DokuWiki.'; $lang['i_policy'] = 'Politica ACL inicial'; $lang['i_pol0'] = 'Wiki Aberto (ler, escrever e carregar para todos)'; $lang['i_pol1'] = 'Wiki Público (ler para todos, escrever e carregar para utilizadores inscritos)'; diff --git a/inc/lang/ro/lang.php b/inc/lang/ro/lang.php index 41727e521..cb8e2ed0c 100644 --- a/inc/lang/ro/lang.php +++ b/inc/lang/ro/lang.php @@ -280,8 +280,8 @@ $lang['i_confexists'] = '%s există deja'; $lang['i_writeerr'] = 'Nu s-a putut crea %s. Trebuie să verificaţi drepturile directorului/fişierului şi să creaţi fişierul manual.'; $lang['i_badhash'] = 'dokuwiki.php nu a fost recunoscut sau a fost modificat (hash=%s)'; $lang['i_badval'] = '%s - valoare nepemisă sau neintrodusă'; -$lang['i_success'] = 'Configurarea a fost finalizată cu succes. Acum puteţi sterge fişierul install.php. Continuaţi cu your new DokuWiki.'; -$lang['i_failure'] = 'Au apărut erori la scrierea fişierelor de configurare. Va trebui să le corectaţi manual înainte de a putea folosi your new DokuWiki.'; +$lang['i_success'] = 'Configurarea a fost finalizată cu succes. Acum puteţi sterge fişierul install.php. Continuaţi cu your new DokuWiki.'; +$lang['i_failure'] = 'Au apărut erori la scrierea fişierelor de configurare. Va trebui să le corectaţi manual înainte de a putea folosi your new DokuWiki.'; $lang['i_policy'] = 'Politica ACL iniţială'; $lang['i_pol0'] = 'Wiki Deschisă (citeşte, scrie şi încarcă oricine)'; $lang['i_pol1'] = 'Wiki Deschisă (citeste oricine, scrie şi încarcă doar utilizatorul înregistrat)'; diff --git a/inc/lang/ru/lang.php b/inc/lang/ru/lang.php index 4c3d26b1d..920bc907f 100644 --- a/inc/lang/ru/lang.php +++ b/inc/lang/ru/lang.php @@ -291,8 +291,8 @@ $lang['i_writeerr'] = 'Не удалось создать %sсвоей новой «ДокуВики».'; +$lang['i_failure'] = 'При записи в файлы конфигурации были обнаружены ошибки. Возможно, вам придётся исправить их вручную, прежде чем вы сможете использовать свою новую «ДокуВики».'; $lang['i_policy'] = 'Исходная политика прав доступа'; $lang['i_pol0'] = 'Открытая вики (чтение, запись, закачка файлов для всех)'; $lang['i_pol1'] = 'Общедоступная вики (чтение для всех, запись и загрузка файлов для зарегистрированных пользователей)'; diff --git a/inc/lang/sk/lang.php b/inc/lang/sk/lang.php index 85e899b06..79a13ac35 100644 --- a/inc/lang/sk/lang.php +++ b/inc/lang/sk/lang.php @@ -268,8 +268,8 @@ $lang['i_confexists'] = '%s už existuje'; $lang['i_writeerr'] = 'Nie je možné vytvoriť %s. Potrebujete skontrolovať prístupové práva pre adresár/súbor a vytvoriť ho manuálne.'; $lang['i_badhash'] = 'neznámy alebo zmenený súbor dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - nesprávna alebo žiadna hodnota'; -$lang['i_success'] = 'Konfigurácia bola úspešne ukončená. Teraz môžete zmazať súbor install.php. Pokračujte vo vašej novej DokuWiki.'; -$lang['i_failure'] = 'Pri zápise konfiguračného súboru nastali nejaké chyby. Potrebujete ich opraviť manuálne pred tým, ako budete môcť používať vašu novú DokuWiki.'; +$lang['i_success'] = 'Konfigurácia bola úspešne ukončená. Teraz môžete zmazať súbor install.php. Pokračujte vo vašej novej DokuWiki.'; +$lang['i_failure'] = 'Pri zápise konfiguračného súboru nastali nejaké chyby. Potrebujete ich opraviť manuálne pred tým, ako budete môcť používať vašu novú DokuWiki.'; $lang['i_policy'] = 'Počiatočná ACL politika'; $lang['i_pol0'] = 'Otvorená Wiki (čítanie, zápis a nahrávanie pre každého)'; $lang['i_pol1'] = 'Verejná Wiki (čítanie pre každého, zápis a nahrávanie pre registrovaných užívateľov)'; diff --git a/inc/lang/sl/lang.php b/inc/lang/sl/lang.php index b34ab67e4..7cd16f7ed 100644 --- a/inc/lang/sl/lang.php +++ b/inc/lang/sl/lang.php @@ -271,8 +271,8 @@ $lang['i_confexists'] = 'Predmet %s že obstaja.'; $lang['i_writeerr'] = 'Ni mogoče ustvariti predmeta %s. Preveriti je treba dovoljenja datotek in map in nato ustvariti datoteko ročno.'; $lang['i_badhash'] = 'nepoznana ali spremenjena datoteka dokuwiki.php (razpršilo=%s)'; $lang['i_badval'] = '%s - neveljavna ali prazna vrednost'; -$lang['i_success'] = 'Nastavitev je uspešno končana. Datoteko install.php lahko sedaj izbrišete. Nadaljujte v novi DokuWiki.'; -$lang['i_failure'] = 'Med zapisovanjem nastavitvenih datotek je prišlo do napak. Preden lahko uporabite vaš DokuWiki, jih je treba odpraviti.'; +$lang['i_success'] = 'Nastavitev je uspešno končana. Datoteko install.php lahko sedaj izbrišete. Nadaljujte v novi DokuWiki.'; +$lang['i_failure'] = 'Med zapisovanjem nastavitvenih datotek je prišlo do napak. Preden lahko uporabite vaš DokuWiki, jih je treba odpraviti.'; $lang['i_policy'] = 'Začetna določila ACL'; $lang['i_pol0'] = 'Odprt Wiki (branje, zapis, nalaganje datotek je javno za vse)'; $lang['i_pol1'] = 'Javni Wiki (branje za vse, zapis in nalaganje datotek za prijavljene uporabnike)'; diff --git a/inc/lang/sq/lang.php b/inc/lang/sq/lang.php index 0e56b89d9..3c58d708d 100644 --- a/inc/lang/sq/lang.php +++ b/inc/lang/sq/lang.php @@ -223,8 +223,8 @@ $lang['i_confexists'] = '%s ekziston njëherë'; $lang['i_writeerr'] = '%s nuk mundi të krijohej. Duhet të kontrolloni lejet e dirkektorisë/skedarit dhe ta krijoni skedarin manualisht.'; $lang['i_badhash'] = 'dokuwiki.php e panjohur ose e ndryshuar (hash=code>%s)'; $lang['i_badval'] = '%s - vlerë e palejuar ose boshe'; -$lang['i_success'] = 'Konfigurimi u mbarua me sukses. Tani mund ta fshini skedarin install.php. Vazhdoni tek DokuWiki juaj i ri..'; -$lang['i_failure'] = 'Ndodhën disa gabime gjatë shkrimit të skedarit të konfigurimit. Do t\'ju duhet t\'i rregulloni manualisht para se të përdorni DokuWiki-in tuaj të ri..'; +$lang['i_success'] = 'Konfigurimi u mbarua me sukses. Tani mund ta fshini skedarin install.php. Vazhdoni tek DokuWiki juaj i ri..'; +$lang['i_failure'] = 'Ndodhën disa gabime gjatë shkrimit të skedarit të konfigurimit. Do t\'ju duhet t\'i rregulloni manualisht para se të përdorni DokuWiki-in tuaj të ri..'; $lang['i_policy'] = 'Veprimi fillestar ACL'; $lang['i_pol0'] = 'Wiki i Hapur (lexim, shkrim, ngarkim për këdo)'; $lang['i_pol1'] = 'Wiki Publike (lexim për këdo, shkrim dhe ngarkim për përdoruesit e regjistruar)'; diff --git a/inc/lang/sr/lang.php b/inc/lang/sr/lang.php index a53f14ac9..875b1d695 100644 --- a/inc/lang/sr/lang.php +++ b/inc/lang/sr/lang.php @@ -244,8 +244,8 @@ $lang['i_confexists'] = '%s већ постоји'; $lang['i_writeerr'] = 'Не могу да направим %s. Проверите дозволе а затим ручно направите ову датотеку.'; $lang['i_badhash'] = 'dokuwiki.php није препознат или је измењен (hash=%s)'; $lang['i_badval'] = '%s - недозвољена или празна вредност'; -$lang['i_success'] = 'Подешавања су завршена. Сада можете обрисати датотеку install.php. Наставите у Ваш нови DokuWiki.'; -$lang['i_failure'] = 'Појавили су се проблеми при писању датотеке са подешавањима. Требало би да их ручно исправите пре него што ћете моћи да користите Ваш нови DokuWiki.'; +$lang['i_success'] = 'Подешавања су завршена. Сада можете обрисати датотеку install.php. Наставите у Ваш нови DokuWiki.'; +$lang['i_failure'] = 'Појавили су се проблеми при писању датотеке са подешавањима. Требало би да их ручно исправите пре него што ћете моћи да користите Ваш нови DokuWiki.'; $lang['i_policy'] = 'Иницијалне корисничке дозволе'; $lang['i_pol0'] = 'Отворени вики (читање, писање, слање датотека за све)'; $lang['i_pol1'] = 'Јавни вики (читање за све, писање и слање датотека само за регистроване кориснике)'; diff --git a/inc/lang/sv/lang.php b/inc/lang/sv/lang.php index 0c8f3276d..4cc97e8c6 100644 --- a/inc/lang/sv/lang.php +++ b/inc/lang/sv/lang.php @@ -246,9 +246,9 @@ $lang['i_writeerr'] = 'Kan inte skapa %s. Kontrollera fi $lang['i_badhash'] = 'okänd eller ändrad dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - felaktig eller blank'; $lang['i_success'] = 'Konfigurationen avslutades utan fel. Du kan radera filen install.php nu. Fortsätt till - din nya DokuWiki.'; + din nya DokuWiki.'; $lang['i_failure'] = 'Fel uppstod vid skrivning av konfigurationsfilerna. Du kan behöva ordna till dem manuellt innan - du kan använda din nya DokuWiki.'; + du kan använda din nya DokuWiki.'; $lang['i_policy'] = 'Initial ACL-policy'; $lang['i_pol0'] = 'Öppen wiki (alla får läsa, skriva och ladda upp filer)'; $lang['i_pol1'] = 'Publik wiki (alla får läsa, registrerade användare för skriva och ladda upp filer)'; diff --git a/inc/lang/tr/lang.php b/inc/lang/tr/lang.php index 77518ac36..cf75aac9d 100644 --- a/inc/lang/tr/lang.php +++ b/inc/lang/tr/lang.php @@ -225,8 +225,8 @@ $lang['i_confexists'] = '%s zaten var'; $lang['i_writeerr'] = '%s oluşturulamadı. Dosya/Klasör izin ayarlarını gözden geçirip dosyayı elle oluşturmalısınız.'; $lang['i_badhash'] = 'dokuwiki.php tanınamadı ya da değiştirilmiş (hash=%s)'; $lang['i_badval'] = '%s - Yanlış veya boş değer'; -$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; -$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; +$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; +$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; $lang['i_policy'] = 'İlk ACL ayarı'; $lang['i_pol0'] = 'Tamamen Açık Wiki (herkes okuyabilir, yazabilir ve dosya yükleyebilir)'; $lang['i_pol1'] = 'Açık Wiki (herkes okuyabilir, ancak sadece üye olanlar yazabilir ve dosya yükleyebilir)'; diff --git a/inc/lang/uk/lang.php b/inc/lang/uk/lang.php index b06cb9158..e38db2300 100644 --- a/inc/lang/uk/lang.php +++ b/inc/lang/uk/lang.php @@ -253,9 +253,9 @@ $lang['i_writeerr'] = 'Неможливо створити %s%s)'; $lang['i_badval'] = '%s - невірне або пусте значення.'; $lang['i_success'] = 'Налаштування завершено. Ви можете знищити файл install.php. -Перейдіть до вашої нової ДокуВікі'; +Перейдіть до вашої нової ДокуВікі'; $lang['i_failure'] = 'При збереженні файлу конфігурації виникли помилки. Можливо вам доведеться виправити їх самостійно -до початку використання вашої нової ДокуВікі.'; +до початку використання вашої нової ДокуВікі.'; $lang['i_policy'] = 'Початкова політика ACL'; $lang['i_pol0'] = 'Відкрита Вікі (читання, запис та завантаження файлів для всіх)'; $lang['i_pol1'] = 'Публічна Вікі (читання для всіх, запис та завантаження для зареєстрованих користувачів)'; diff --git a/inc/lang/zh-tw/lang.php b/inc/lang/zh-tw/lang.php index bbbbb71a6..41c79f57c 100644 --- a/inc/lang/zh-tw/lang.php +++ b/inc/lang/zh-tw/lang.php @@ -272,8 +272,8 @@ $lang['i_writeerr'] = '無法建立 %s。您必須檢查 $lang['i_badhash'] = '無法辨識或被變更的 dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - 非法或空白的值'; $lang['i_success'] = '設定已成功完成。您現在可以刪除 install.php 檔案。繼續到 -您的新 DokuWiki.'; -$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; +您的新 DokuWiki.'; +$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; $lang['i_policy'] = '初步的 ACL 政策'; $lang['i_pol0'] = '開放的維基 (任何人可讀取、寫入、上傳)'; $lang['i_pol1'] = '公開的維基 (任何人可讀取,註冊使用者可寫入與上傳)'; diff --git a/inc/lang/zh/lang.php b/inc/lang/zh/lang.php index 9ea0f5e7f..8b9644262 100644 --- a/inc/lang/zh/lang.php +++ b/inc/lang/zh/lang.php @@ -288,8 +288,8 @@ $lang['i_writeerr'] = '无法创建 %s。您需要检查 $lang['i_badhash'] = '无法识别的或被修改的 dokuwiki.php(值=%s)'; $lang['i_badval'] = '%s - 非法或空值'; $lang['i_success'] = '配置成功完成。您现在可以删除 install.php 了。继续进入 - 您全新的 DokuWiki。'; -$lang['i_failure'] = '写入配置文件的时候产生一些错误。在使用 您全新安装的 DokuWiki 前 + 您全新的 DokuWiki。'; +$lang['i_failure'] = '写入配置文件的时候产生一些错误。在使用 您全新安装的 DokuWiki 前 您需要手动修复它们。'; $lang['i_policy'] = '初始的 ACL 政策'; $lang['i_pol0'] = '开放的维基(任何人都有读、写、上传的权限)'; -- cgit v1.2.3 From b1730bd21bd9ce8807b80c4cfb044858c7af60d8 Mon Sep 17 00:00:00 2001 From: Tom N Harris Date: Tue, 17 Jul 2012 19:45:48 -0400 Subject: Translatable string for "None" license selection The position of the option was moved to the end of the list so the English label "None of the above" will make sense. --- inc/lang/en/lang.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc') diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 3c51ba224..2592b9a5c 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -311,6 +311,7 @@ $lang['i_pol1'] = 'Public Wiki (read for everyone, write and uplo $lang['i_pol2'] = 'Closed Wiki (read, write, upload for registered users only)'; $lang['i_retry'] = 'Retry'; $lang['i_license'] = 'Please choose the license you want to put your content under:'; +$lang['i_license_none'] = 'None of the above'; $lang['recent_global'] = 'You\'re currently watching the changes inside the %s namespace. You can also view the recent changes of the whole wiki.'; $lang['years'] = '%d years ago'; -- cgit v1.2.3 From 1ea7a6bada66fc9b7a45f61b4892e4ea23196d89 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 28 Jul 2012 14:55:45 +0200 Subject: support CONNECT for tunneling SSL via HTTP proxies FS#2431 The included test cases currently expect a squid at localhost:3128 --- inc/HTTPClient.php | 66 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/HTTPClient.php b/inc/HTTPClient.php index a25846c31..8d2c10be1 100644 --- a/inc/HTTPClient.php +++ b/inc/HTTPClient.php @@ -254,11 +254,7 @@ class HTTPClient { if(!empty($uri['port'])) $headers['Host'].= ':'.$uri['port']; $headers['User-Agent'] = $this->agent; $headers['Referer'] = $this->referer; - if ($this->keep_alive) { - $headers['Connection'] = 'Keep-Alive'; - } else { - $headers['Connection'] = 'Close'; - } + if($method == 'POST'){ if(is_array($data)){ if($headers['Content-Type'] == 'multipart/form-data'){ @@ -299,6 +295,14 @@ class HTTPClient { return false; } + // try establish a CONNECT tunnel for SSL + if($this->_ssltunnel($socket, $request_url)){ + // no keep alive for tunnels + $this->keep_alive = false; + // tunnel is authed already + if(isset($headers['Proxy-Authentication'])) unset($headers['Proxy-Authentication']); + } + // keep alive? if ($this->keep_alive) { self::$connections[$connectionId] = $socket; @@ -307,6 +311,15 @@ class HTTPClient { } } + if ($this->keep_alive && !$this->proxy_host) { + // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive + // connection token to a proxy server. We still do keep the connection the + // proxy alive (well except for CONNECT tunnels) + $headers['Connection'] = 'Keep-Alive'; + } else { + $headers['Connection'] = 'Close'; + } + try { //set non-blocking stream_set_blocking($socket, false); @@ -484,6 +497,49 @@ class HTTPClient { return true; } + /** + * Tries to establish a CONNECT tunnel via Proxy + * + * Protocol, Servername and Port will be stripped from the request URL when a successful CONNECT happened + * + * @param ressource &$socket + * @param string &$requesturl + * @return bool true if a tunnel was established + */ + function _ssltunnel(&$socket, &$requesturl){ + if(!$this->proxy_host) return false; + $requestinfo = parse_url($requesturl); + if($requestinfo['scheme'] != 'https') return false; + if(!$requestinfo['port']) $requestinfo['port'] = 443; + + // build request + $request = "CONNECT {$requestinfo['host']}:{$requestinfo['port']} HTTP/1.0".HTTP_NL; + $request .= "Host: {$requestinfo['host']}".HTTP_NL; + if($this->proxy_user) { + 'Proxy-Authorization Basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass).HTTP_NL; + } + $request .= HTTP_NL; + + $this->_debug('SSL Tunnel CONNECT',$request); + $this->_sendData($socket, $request, 'SSL Tunnel CONNECT'); + + // read headers from socket + $r_headers = ''; + do{ + $r_line = $this->_readLine($socket, 'headers'); + $r_headers .= $r_line; + }while($r_line != "\r\n" && $r_line != "\n"); + + $this->_debug('SSL Tunnel Response',$r_headers); + if(preg_match('/^HTTP\/1\.0 200/i',$r_headers)){ + if (stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_SSLv3_CLIENT)) { + $requesturl = $requestinfo['path']; + return true; + } + } + return false; + } + /** * Safely write data to a socket * -- cgit v1.2.3 From 2240ea1f316156f3cb4475ea23a16246c999b6f0 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 12:00:37 +0200 Subject: first start at refactoring the subscription system BROKEN This introduces a class for nicer wrapping and easier testing. Some functions were changed to provide nicer APIs (no throwing around of unescaped regexps) and to simplify things (hopefully). The refactoring isn't completed yet, so this will break the subscription system. The goal is to move as much subscription related stuff to this class as possible. Currently there is some code in lib/exe/indexer.php and maybe elsewhere (common.php?). Additionally everything should be covered by tests. A few tests are included here already. --- inc/load.php | 1 + inc/subscription.php | 724 ++++++++++++++++++++++++++------------------------- 2 files changed, 376 insertions(+), 349 deletions(-) (limited to 'inc') diff --git a/inc/load.php b/inc/load.php index b676518e7..bd6d39814 100644 --- a/inc/load.php +++ b/inc/load.php @@ -81,6 +81,7 @@ function load_autoload($name){ 'Mailer' => DOKU_INC.'inc/Mailer.class.php', 'RemoteAPI' => DOKU_INC.'inc/remote.php', 'RemoteAPICore' => DOKU_INC.'inc/RemoteAPICore.php', + 'Subscription' => DOKU_INC.'inc/subscription.php', 'DokuWiki_Action_Plugin' => DOKU_PLUGIN.'action.php', 'DokuWiki_Admin_Plugin' => DOKU_PLUGIN.'admin.php', diff --git a/inc/subscription.php b/inc/subscription.php index 1f1aedfa4..856836cd5 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -12,399 +12,425 @@ * - subscription_lock * - subscription_unlock * + * @fixme handle $conf['subscribers'] and disable actions + * * @author Adrian Lang + * @author Andreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) */ -/** - * Get the name of the metafile tracking subscriptions to target page or - * namespace - * - * @author Adrian Lang - * - * @param string $id The target page or namespace, specified by id; Namespaces - * are identified by appending a colon. - * @return string - */ -function subscription_filename($id) { - $meta_fname = '.mlist'; - if ((substr($id, -1, 1) === ':')) { - $meta_froot = getNS($id); - $meta_fname = '/' . $meta_fname; - } else { - $meta_froot = $id; +class Subscription { + + /** + * Return the subscription meta file for the given ID + * + * @author Adrian Lang + * + * @param string $id The target page or namespace, specified by id; Namespaces + * are identified by appending a colon. + * @return string + */ + protected function file($id) { + $meta_fname = '.mlist'; + if((substr($id, -1, 1) === ':')) { + $meta_froot = getNS($id); + $meta_fname = '/'.$meta_fname; + } else { + $meta_froot = $id; + } + return metaFN((string) $meta_froot, $meta_fname); } - return metaFN((string) $meta_froot, $meta_fname); -} -/** - * Lock subscription info for an ID - * - * @author Adrian Lang - * @param string $id The target page or namespace, specified by id; Namespaces - * are identified by appending a colon. - * @return string - */ -function subscription_lock_filename ($id){ - global $conf; - return $conf['lockdir'].'/_subscr_' . md5($id) . '.lock'; -} + /** + * Lock subscription info + * + * We don't use io_lock() her because we do not wait for the lock and use a larger stale time + * + * @author Adrian Lang + * @param string $id The target page or namespace, specified by id; Namespaces + * are identified by appending a colon. + * @return bool true, if you got a succesful lock + */ + protected function lock($id) { + global $conf; -/** - * Creates a lock file for writing subscription data - * - * @todo add lock time parameter to io_lock() and use this instead - * @param $id - * @return bool - */ -function subscription_lock($id) { - global $conf; - $lock = subscription_lock_filename($id); + $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock'; - if (is_dir($lock) && time()-@filemtime($lock) > 60*5) { - // looks like a stale lock - remove it - @rmdir($lock); + if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) { + // looks like a stale lock - remove it + @rmdir($lock); + } + + // try creating the lock directory + if(!@mkdir($lock, $conf['dmode'])) { + return false; + } + + if($conf['dperm']) chmod($lock, $conf['dperm']); + return true; } - // try creating the lock directory - if (!@mkdir($lock,$conf['dmode'])) { - return false; + /** + * Unlock subscription info + * + * @author Adrian Lang + * @param string $id The target page or namespace, specified by id; Namespaces + * are identified by appending a colon. + * @return bool + */ + protected function unlock($id) { + global $conf; + $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock'; + return @rmdir($lock); } - if($conf['dperm']) chmod($lock, $conf['dperm']); - return true; -} + /** + * Construct a regular expression for parsing a subscription definition line + * + * @author Andreas Gohr + * + * @param string|array $user + * @param string|array $style + * @param string|array $data + * @return string complete regexp including delimiters + * @throws Exception when no data is passed + */ + protected function buildregex($user = null, $style = null, $data = null) { + // always work with arrays + $user = (array) $user; + $style = (array) $style; + $data = (array) $data; -/** - * Unlock subscription info for an ID - * - * @author Adrian Lang - * @param string $id The target page or namespace, specified by id; Namespaces - * are identified by appending a colon. - * @return bool - */ -function subscription_unlock($id) { - $lockf = subscription_lock_filename($id); - return @rmdir($lockf); -} + // clean + $user = array_filter(array_map('trim', $user)); + $style = array_filter(array_map('trim', $style)); + $data = array_filter(array_map('trim', $data)); -/** - * Set subscription information - * - * Allows to set subscription information for permanent storage in meta files. - * Subscriptions consist of a target object, a subscribing user, a subscribe - * style and optional data. - * A subscription may be deleted by specifying an empty subscribe style. - * Only one subscription per target and user is allowed. - * The function returns false on error, otherwise true. Note that no error is - * returned if a subscription should be deleted but the user is not subscribed - * and the subscription meta file exists. - * - * @author Adrian Lang - * - * @param string $user The subscriber or unsubscriber - * @param string $page The target object (page or namespace), specified by - * id; Namespaces are identified by a trailing colon. - * @param string $style The subscribe style; DokuWiki currently implements - * “every”, “digest”, and “list”. - * @param string $data An optional data blob - * @param bool $overwrite Whether an existing subscription may be overwritten - * @return bool - */ -function subscription_set($user, $page, $style, $data = null, - $overwrite = false) { - global $lang; - if (is_null($style)) { - // Delete subscription. - $file = subscription_filename($page); - if (!@file_exists($file)) { - msg(sprintf($lang['subscr_not_subscribed'], $user, - prettyprint_id($page)), -1); - return false; - } + // user names are encoded + $user = array_map('auth_nameencode', $user); - // io_deleteFromFile does not return false if no line matched. - return io_deleteFromFile($file, - subscription_regex(array('user' => auth_nameencode($user))), - true); - } + // quote + $user = array_map('preg_quote_cb', $user); + $style = array_map('preg_quote_cb', $style); + $data = array_map('preg_quote_cb', $data); - // Delete subscription if one exists and $overwrite is true. If $overwrite - // is false, fail. - $subs = subscription_find($page, array('user' => $user)); - if (count($subs) > 0 && array_pop(array_keys($subs)) === $page) { - if (!$overwrite) { - msg(sprintf($lang['subscr_already_subscribed'], $user, - prettyprint_id($page)), -1); - return false; + // join + $user = join('|', $user); + $style = join('|', $style); + $data = join('|', $data); + + // any data at all? + if($user.$style.$data === '') throw new Exception('no data passed'); + + // replace empty values, set which ones are optional + $sopt = ''; + $dopt = ''; + if($user === '') { + $user = '\S+'; } - // Fail if deletion failed, else continue. - if (!subscription_set($user, $page, null)) { - return false; + if($style === '') { + $style = '\S+'; + $sopt = '?'; + } + if($data === '') { + $data = '\S+'; + $dopt = '?'; } - } - $file = subscription_filename($page); - $content = auth_nameencode($user) . ' ' . $style; - if (!is_null($data)) { - $content .= ' ' . $data; + // assemble + return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/"; } - return io_saveFile($file, $content . "\n", true); -} -/** - * Recursively search for matching subscriptions - * - * This function searches all relevant subscription files for a page or - * namespace. - * - * @author Adrian Lang - * @see function subscription_regex for $pre documentation - * - * @param string $page The target object’s (namespace or page) id - * @param array $pre A hash of predefined values - * @return array - */ -function subscription_find($page, $pre) { - // Construct list of files which may contain relevant subscriptions. - $filenames = array(':' => subscription_filename(':')); - do { - $filenames[$page] = subscription_filename($page); - $page = getNS(rtrim($page, ':')) . ':'; - } while ($page !== ':'); - - // Handle files. - $matches = array(); - foreach ($filenames as $cur_page => $filename) { - if (!@file_exists($filename)) { - continue; - } - $subscriptions = file($filename); - foreach ($subscriptions as $subscription) { - if (strpos($subscription, ' ') === false) { - // This is an old subscription file. - $subscription = trim($subscription) . " every\n"; - } + /** + * Recursively search for matching subscriptions + * + * This function searches all relevant subscription files for a page or + * namespace. + * + * @author Adrian Lang + * + * @param string $page The target object’s (namespace or page) id + * @param string|array $user + * @param string|array $style + * @param string|array $data + * @return array + */ + public function subscribers($page, $user = null, $style = null, $data = null) { + // Construct list of files which may contain relevant subscriptions. + $files = array(':' => $this->file(':')); + do { + $files[$page] = $this->file($page); + $page = getNS(rtrim($page, ':')).':'; + } while($page !== ':'); - list($user, $rest) = explode(' ', $subscription, 2); - $subscription = rawurldecode($user) . " " . $rest; + $re = $this->buildregex($user, $style, $data); - if (preg_match(subscription_regex($pre), $subscription, - $line_matches) === 0) { - continue; - } - $match = array_slice($line_matches, 1); - if (!isset($matches[$cur_page])) { - $matches[$cur_page] = array(); + // Handle files. + $result = array(); + foreach($files as $target => $file) { + if(!@file_exists($file)) continue; + + $lines = file($file); + foreach($lines as $line) { + // fix old style subscription files + if(strpos($line, ' ') === false) $line = trim($line)." every\n"; + + // check for matching entries + if(!preg_match($re, $line, $m)) continue; + + $u = rawurldecode($m[1]); // decode the user name + if(!isset($result[$target])) $result[$target] = array(); + $result[$target][$u] = array($m[2], $m[3]); // add to result } - $matches[$cur_page][] = $match; } + return array_reverse($result); } - return array_reverse($matches); -} -/** - * Get data for $INFO['subscribed'] - * - * $INFO['subscribed'] is either false if no subscription for the current page - * and user is in effect. Else it contains an array of arrays with the fields - * “target”, “style”, and optionally “data”. - * - * @author Adrian Lang - */ -function get_info_subscribed() { - global $ID; - global $conf; - if (!$conf['subscribers']) { - return false; - } + /** + * Adds a new subscription for the given page or namespace + * + * This will automatically overwrite any existent subscription for the given user on this + * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces. + * + * @param string $id The target page or namespace, specified by id; Namespaces + * are identified by appending a colon. + * @param string $user + * @param string $style + * @param string $data + * @throws Exception when user or style is empty + * @return bool + */ + public function add($id, $user, $style, $data = '') { + // delete any existing subscription + $this->remove($id, $user); - $subs = subscription_find($ID, array('user' => $_SERVER['REMOTE_USER'])); - if (count($subs) === 0) { - return false; - } + $user = auth_nameencode(trim($user)); + $style = trim($style); + $data = trim($data); - $_ret = array(); - foreach ($subs as $target => $subs_data) { - $new = array('target' => $target, - 'style' => $subs_data[0][0]); - if (count($subs_data[0]) > 1) { - $new['data'] = $subs_data[0][1]; - } - $_ret[] = $new; + if(!$user) throw new Exception('no subscription user given'); + if(!$style) throw new Exception('no subscription style given'); + + $line = "$user $style"; + if($data) $line .= " $data"; + $line .= "\n"; + + $file = $this->file($id); + + return io_saveFile($file, $line, true); } - return $_ret; -} + /** + * Removes a subscription for the given page or namespace + * + * This removes all subscriptions matching the given criteria on the given page or + * namespace. It will *not* modify any subscriptions that may exist in higher + * namespaces. + * + * @param string $id The target object’s (namespace or page) id + * @param string|array $user + * @param string|array $style + * @param string|array $data + * @return bool + */ + public function remove($id, $user = null, $style = null, $data = null) { + $file = $this->file($id); + if(!file_exists($file)) return true; -/** - * Construct a regular expression parsing a subscription definition line - * - * @author Adrian Lang - * - * @param array $pre A hash of predefined values; “user”, “style”, and - * “data” may be set to limit the results to - * subscriptions matching these parameters. If - * “escaped” is true, these fields are inserted into the - * regular expression without escaping. - * - * @return string complete regexp including delimiters - */ -function subscription_regex($pre = array()) { - if (!isset($pre['escaped']) || $pre['escaped'] === false) { - $pre = array_map('preg_quote_cb', $pre); + $re = $this->buildregex($user, $style, $data); + return io_deleteFromFile($file, $re, true); } - foreach (array('user', 'style', 'data') as $key) { - if (!isset($pre[$key])) { - $pre[$key] = '(\S+)'; + + /** + * Get data for $INFO['subscribed'] + * + * $INFO['subscribed'] is either false if no subscription for the current page + * and user is in effect. Else it contains an array of arrays with the fields + * “target”, “style”, and optionally “data”. + * + * @param string $id Page ID, defaults to global $ID + * @param string $user User, defaults to $_SERVER['REMOTE_USER'] + * @return array + * @author Adrian Lang + */ + function user_subscription($id='', $user='') { + global $ID; + global $conf; + if(!$conf['subscribers']) return false; + + if(!$id) $id = $ID; + if(!$user) $user = $_SERVER['REMOTE_USER']; + + + $subs = $this->subscribers($id, $user); + if(!count($subs)) return false; + + $result = array(); + foreach($subs as $target => $data) { + $result[] = array( + 'target' => $target, + 'style' => $data[$user][0], + 'data' => $data[$user][1] + ); } + + return $result; } - return '/^' . $pre['user'] . '(?: ' . $pre['style'] . - '(?: ' . $pre['data'] . ')?)?$/'; -} -/** - * Return a string with the email addresses of all the - * users subscribed to a page - * - * This is the default action for COMMON_NOTIFY_ADDRESSLIST. - * - * @author Steven Danz - * @author Adrian Lang - * - * @todo this does NOT return a string but uses a reference to write back, either fix function or docs - * @param array $data Containing $id (the page id), $self (whether the author - * should be notified, $addresslist (current email address - * list) - * @return string - */ -function subscription_addresslist(&$data){ - global $conf; - /** @var auth_basic $auth */ - global $auth; + /** + * Return a string with the email addresses of all the + * users subscribed to a page + * + * This is the default action for COMMON_NOTIFY_ADDRESSLIST. + * + * @author Steven Danz + * @author Adrian Lang + * + * @todo this does NOT return a string but uses a reference to write back, either fix function or docs + * @param array $data Containing $id (the page id), $self (whether the author + * should be notified, $addresslist (current email address + * list) + * @return string + */ + function subscription_addresslist(&$data) { + global $conf; + /** @var auth_basic $auth */ + global $auth; - $id = $data['id']; - $self = $data['self']; - $addresslist = $data['addresslist']; + $id = $data['id']; + $self = $data['self']; + $addresslist = $data['addresslist']; - if (!$conf['subscribers'] || $auth === null) { - return ''; - } - $pres = array('style' => 'every', 'escaped' => true); - if (!$self && isset($_SERVER['REMOTE_USER'])) { - $pres['user'] = '((?!' . preg_quote_cb($_SERVER['REMOTE_USER']) . - '(?: |$))\S+)'; - } - $subs = subscription_find($id, $pres); - $emails = array(); - foreach ($subs as $by_targets) { - foreach ($by_targets as $sub) { - $info = $auth->getUserData($sub[0]); - if ($info === false) continue; - $level = auth_aclcheck($id, $sub[0], $info['grps']); - if ($level >= AUTH_READ) { - if (strcasecmp($info['mail'], $conf['notify']) != 0) { - $emails[$sub[0]] = $info['mail']; + if(!$conf['subscribers'] || $auth === null) { + return ''; + } + $pres = array('style' => 'every', 'escaped' => true); + if(!$self && isset($_SERVER['REMOTE_USER'])) { + $pres['user'] = '((?!'.preg_quote_cb($_SERVER['REMOTE_USER']). + '(?: |$))\S+)'; + } + $subs = subscription_find($id, $pres); + $emails = array(); + foreach($subs as $by_targets) { + foreach($by_targets as $sub) { + $info = $auth->getUserData($sub[0]); + if($info === false) continue; + $level = auth_aclcheck($id, $sub[0], $info['grps']); + if($level >= AUTH_READ) { + if(strcasecmp($info['mail'], $conf['notify']) != 0) { + $emails[$sub[0]] = $info['mail']; + } } } } + $data['addresslist'] = trim($addresslist.','.implode(',', $emails), ','); } - $data['addresslist'] = trim($addresslist . ',' . implode(',', $emails), ','); -} -/** - * Send a digest mail - * - * Sends a digest mail showing a bunch of changes. - * - * @author Adrian Lang - * - * @param string $subscriber_mail The target mail address - * @param array $id The ID - * @param int $lastupdate Time of the last notification - */ -function subscription_send_digest($subscriber_mail, $id, $lastupdate) { - $n = 0; - do { - $rev = getRevisions($id, $n++, 1); - $rev = (count($rev) > 0) ? $rev[0] : null; - } while (!is_null($rev) && $rev > $lastupdate); - - $replaces = array('NEWPAGE' => wl($id, '', true, '&'), - 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')); - if (!is_null($rev)) { - $subject = 'changed'; - $replaces['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); - $df = new Diff(explode("\n", rawWiki($id, $rev)), - explode("\n", rawWiki($id))); - $dformat = new UnifiedDiffFormatter(); - $replaces['DIFF'] = $dformat->format($df); - } else { - $subject = 'newpage'; - $replaces['OLDPAGE'] = 'none'; - $replaces['DIFF'] = rawWiki($id); + /** + * Send a digest mail + * + * Sends a digest mail showing a bunch of changes. + * + * @author Adrian Lang + * + * @param string $subscriber_mail The target mail address + * @param array $id The ID + * @param int $lastupdate Time of the last notification + */ + function subscription_send_digest($subscriber_mail, $id, $lastupdate) { + $n = 0; + do { + $rev = getRevisions($id, $n++, 1); + $rev = (count($rev) > 0) ? $rev[0] : null; + } while(!is_null($rev) && $rev > $lastupdate); + + $replaces = array( + 'NEWPAGE' => wl($id, '', true, '&'), + 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') + ); + if(!is_null($rev)) { + $subject = 'changed'; + $replaces['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); + $df = new Diff(explode("\n", rawWiki($id, $rev)), + explode("\n", rawWiki($id))); + $dformat = new UnifiedDiffFormatter(); + $replaces['DIFF'] = $dformat->format($df); + } else { + $subject = 'newpage'; + $replaces['OLDPAGE'] = 'none'; + $replaces['DIFF'] = rawWiki($id); + } + subscription_send( + $subscriber_mail, $replaces, $subject, $id, + 'subscr_digest' + ); } - subscription_send($subscriber_mail, $replaces, $subject, $id, - 'subscr_digest'); -} -/** - * Send a list mail - * - * Sends a list mail showing a list of changed pages. - * - * @author Adrian Lang - * - * @param string $subscriber_mail The target mail address - * @param array $ids Array of ids - * @param string $ns_id The id of the namespace - */ -function subscription_send_list($subscriber_mail, $ids, $ns_id) { - if (count($ids) === 0) return; - global $conf; - $list = ''; - foreach ($ids as $id) { - $list .= '* ' . wl($id, array(), true) . NL; + /** + * Send a list mail + * + * Sends a list mail showing a list of changed pages. + * + * @author Adrian Lang + * + * @param string $subscriber_mail The target mail address + * @param array $ids Array of ids + * @param string $ns_id The id of the namespace + */ + function subscription_send_list($subscriber_mail, $ids, $ns_id) { + if(count($ids) === 0) return; + global $conf; + $list = ''; + foreach($ids as $id) { + $list .= '* '.wl($id, array(), true).NL; + } + subscription_send( + $subscriber_mail, + array( + 'DIFF' => rtrim($list), + 'SUBSCRIBE' => wl( + $ns_id.$conf['start'], + array('do' => 'subscribe'), + true, '&' + ) + ), + 'subscribe_list', + prettyprint_id($ns_id), + 'subscr_list' + ); } - subscription_send($subscriber_mail, - array('DIFF' => rtrim($list), - 'SUBSCRIBE' => wl($ns_id . $conf['start'], - array('do' => 'subscribe'), - true, '&')), - 'subscribe_list', - prettyprint_id($ns_id), - 'subscr_list'); -} -/** - * Helper function for sending a mail - * - * @author Adrian Lang - * - * @param string $subscriber_mail The target mail address - * @param array $replaces Predefined parameters used to parse the - * template - * @param string $subject The lang id of the mail subject (without the - * prefix “mail_”) - * @param string $id The page or namespace id - * @param string $template The name of the mail template - * @return bool - */ -function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) { - global $lang; - - $text = rawLocale($template); - $trep = array_merge($replaces, array('PAGE' => $id)); - - $subject = $lang['mail_' . $subject] . ' ' . $id; - $mail = new Mailer(); - $mail->bcc($subscriber_mail); - $mail->subject($subject); - $mail->setBody($text,$trep); - $mail->setHeader( - 'List-Unsubscribe', - '<'.wl($id,array('do'=>'subscribe'),true,'&').'>', - false - ); - return $mail->send(); -} + /** + * Helper function for sending a mail + * + * @author Adrian Lang + * + * @param string $subscriber_mail The target mail address + * @param array $replaces Predefined parameters used to parse the + * template + * @param string $subject The lang id of the mail subject (without the + * prefix “mail_”) + * @param string $id The page or namespace id + * @param string $template The name of the mail template + * @return bool + */ + function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) { + global $lang; + + $text = rawLocale($template); + $trep = array_merge($replaces, array('PAGE' => $id)); + + $subject = $lang['mail_'.$subject].' '.$id; + $mail = new Mailer(); + $mail->bcc($subscriber_mail); + $mail->subject($subject); + $mail->setBody($text, $trep); + $mail->setHeader( + 'List-Unsubscribe', + '<'.wl($id, array('do'=> 'subscribe'), true, '&').'>', + false + ); + return $mail->send(); + } + +} \ No newline at end of file -- cgit v1.2.3 From e920a0a10c3027700e61166a6f8d4ea29a9ff102 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 14:00:53 +0200 Subject: handle empty changelog in getRecentsSince() --- inc/changelog.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc') diff --git a/inc/changelog.php b/inc/changelog.php index 24583b341..7ca7c62e9 100644 --- a/inc/changelog.php +++ b/inc/changelog.php @@ -254,6 +254,7 @@ function getRecentsSince($from,$to=null,$ns='',$flags=0){ } else { $lines = @file($conf['changelog']); } + if(!$lines) return $recent; // we start searching at the end of the list $lines = array_reverse($lines); -- cgit v1.2.3 From adec979fd5453cf213b776d7dceaaaac4eb05713 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 15:07:03 +0200 Subject: more subscription refactoring BROKEN now the actual sending of bulk messages (digest, list) is reimplemented and partially tested. Still not complete --- inc/common.php | 4 +- inc/subscription.php | 191 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 149 insertions(+), 46 deletions(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index ac7e744d8..29940d8a6 100644 --- a/inc/common.php +++ b/inc/common.php @@ -107,9 +107,11 @@ function pageinfo() { $info['isadmin'] = false; $info['ismanager'] = false; if(isset($_SERVER['REMOTE_USER'])) { + $sub = new Subscription(); + $info['userinfo'] = $USERINFO; $info['perm'] = auth_quickaclcheck($ID); - $info['subscribed'] = get_info_subscribed(); + $info['subscribed'] = $sub->user_subscription(); $info['client'] = $_SERVER['REMOTE_USER']; if($info['perm'] == AUTH_ADMIN) { diff --git a/inc/subscription.php b/inc/subscription.php index 856836cd5..804776ced 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -253,24 +253,23 @@ class Subscription { * @return array * @author Adrian Lang */ - function user_subscription($id='', $user='') { + function user_subscription($id = '', $user = '') { global $ID; global $conf; if(!$conf['subscribers']) return false; - if(!$id) $id = $ID; + if(!$id) $id = $ID; if(!$user) $user = $_SERVER['REMOTE_USER']; - $subs = $this->subscribers($id, $user); if(!count($subs)) return false; $result = array(); - foreach($subs as $target => $data) { + foreach($subs as $target => $info) { $result[] = array( 'target' => $target, - 'style' => $data[$user][0], - 'data' => $data[$user][1] + 'style' => $info[$user][0], + 'data' => $info[$user][1] ); } @@ -278,52 +277,100 @@ class Subscription { } /** - * Return a string with the email addresses of all the - * users subscribed to a page + * Send digest and list subscriptions * - * This is the default action for COMMON_NOTIFY_ADDRESSLIST. + * This sends mails to all subscribers that have a subscription for namespaces above + * the given page if the needed $conf['subscribe_time'] has passed already. * - * @author Steven Danz - * @author Adrian Lang + * This function is called form lib/exe/indexer.php * - * @todo this does NOT return a string but uses a reference to write back, either fix function or docs - * @param array $data Containing $id (the page id), $self (whether the author - * should be notified, $addresslist (current email address - * list) - * @return string + * @param string $page + * @return int number of sent mails */ - function subscription_addresslist(&$data) { - global $conf; + public function send_bulk($page) { /** @var auth_basic $auth */ global $auth; + global $conf; + global $USERINFO; + $count = 0; - $id = $data['id']; - $self = $data['self']; - $addresslist = $data['addresslist']; + $subscriptions = $this->subscribers($page, null, array('digest', 'list')); - if(!$conf['subscribers'] || $auth === null) { - return ''; - } - $pres = array('style' => 'every', 'escaped' => true); - if(!$self && isset($_SERVER['REMOTE_USER'])) { - $pres['user'] = '((?!'.preg_quote_cb($_SERVER['REMOTE_USER']). - '(?: |$))\S+)'; - } - $subs = subscription_find($id, $pres); - $emails = array(); - foreach($subs as $by_targets) { - foreach($by_targets as $sub) { - $info = $auth->getUserData($sub[0]); - if($info === false) continue; - $level = auth_aclcheck($id, $sub[0], $info['grps']); - if($level >= AUTH_READ) { - if(strcasecmp($info['mail'], $conf['notify']) != 0) { - $emails[$sub[0]] = $info['mail']; + // remember current user info + $olduinfo = $USERINFO; + $olduser = $_SERVER['REMOTE_USER']; + + foreach($subscriptions as $target => $users) { + if(!$this->lock($target)) continue; + + foreach($users as $user => $info) { + list($style, $lastupdate) = $info; + + $lastupdate = (int) $lastupdate; + if($lastupdate + $conf['subscribe_time'] > time()) { + // Less than the configured time period passed since last + // update. + continue; + } + + // Work as the user to make sure ACLs apply correctly + $USERINFO = $auth->getUserData($user); + $_SERVER['REMOTE_USER'] = $user; + if($USERINFO === false) continue; + if(!$USERINFO['mail']) continue; + + if(substr($target, -1, 1) === ':') { + // subscription target is a namespace, get all changes within + $changes = getRecentsSince($lastupdate, null, getNS($target)); + } else { + // single page subscription, check ACL ourselves + if(auth_quickaclcheck($target) < AUTH_READ) continue; + $meta = p_get_metadata($target); + $changes = array($meta['last_change']); + } + + // Filter out pages only changed in small and own edits + $change_ids = array(); + foreach($changes as $rev) { + $n = 0; + while(!is_null($rev) && $rev['date'] >= $lastupdate && + ($_SERVER['REMOTE_USER'] === $rev['user'] || + $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) { + $rev = getRevisions($rev['id'], $n++, 1); + $rev = (count($rev) > 0) ? $rev[0] : null; + } + + if(!is_null($rev) && $rev['date'] >= $lastupdate) { + // Some change was not a minor one and not by myself + $change_ids[] = $rev['id']; } } + + // send it + if($style === 'digest') { + foreach($change_ids as $change_id) { + $this->send_digest( + $USERINFO['mail'], $change_id, + $lastupdate + ); + $count++; + } + } elseif($style === 'list') { + $this->send_list($USERINFO['mail'], $change_ids, $target); + $count++; + } + // TODO: Handle duplicate subscriptions. + + // Update notification time. + $this->add($target, $user, $style, time()); } + $this->unlock($target); } - $data['addresslist'] = trim($addresslist.','.implode(',', $emails), ','); + + // restore current user info + $USERINFO = $olduinfo; + $_SERVER['REMOTE_USER'] = $olduser; + return $count; } /** @@ -337,7 +384,7 @@ class Subscription { * @param array $id The ID * @param int $lastupdate Time of the last notification */ - function subscription_send_digest($subscriber_mail, $id, $lastupdate) { + protected function send_digest($subscriber_mail, $id, $lastupdate) { $n = 0; do { $rev = getRevisions($id, $n++, 1); @@ -360,7 +407,7 @@ class Subscription { $replaces['OLDPAGE'] = 'none'; $replaces['DIFF'] = rawWiki($id); } - subscription_send( + $this->send( $subscriber_mail, $replaces, $subject, $id, 'subscr_digest' ); @@ -377,14 +424,14 @@ class Subscription { * @param array $ids Array of ids * @param string $ns_id The id of the namespace */ - function subscription_send_list($subscriber_mail, $ids, $ns_id) { + protected function send_list($subscriber_mail, $ids, $ns_id) { if(count($ids) === 0) return; global $conf; $list = ''; foreach($ids as $id) { $list .= '* '.wl($id, array(), true).NL; } - subscription_send( + $this->send( $subscriber_mail, array( 'DIFF' => rtrim($list), @@ -414,7 +461,7 @@ class Subscription { * @param string $template The name of the mail template * @return bool */ - function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) { + protected function send($subscriber_mail, $replaces, $subject, $id, $template) { global $lang; $text = rawLocale($template); @@ -433,4 +480,58 @@ class Subscription { return $mail->send(); } + + + // FIXME no refactoring below, yet + + /** + * Return a string with the email addresses of all the + * users subscribed to a page + * + * This is the default action for COMMON_NOTIFY_ADDRESSLIST. + * + * @author Steven Danz + * @author Adrian Lang + * + * @todo this does NOT return a string but uses a reference to write back, either fix function or docs + * @param array $data Containing $id (the page id), $self (whether the author + * should be notified, $addresslist (current email address + * list) + * @return string + */ + function subscription_addresslist(&$data) { + global $conf; + /** @var auth_basic $auth */ + global $auth; + + $id = $data['id']; + $self = $data['self']; + $addresslist = $data['addresslist']; + + if(!$conf['subscribers'] || $auth === null) { + return ''; + } + $pres = array('style' => 'every', 'escaped' => true); + if(!$self && isset($_SERVER['REMOTE_USER'])) { + $pres['user'] = '((?!'.preg_quote_cb($_SERVER['REMOTE_USER']). + '(?: |$))\S+)'; + } + $subs = subscription_find($id, $pres); + $emails = array(); + foreach($subs as $by_targets) { + foreach($by_targets as $sub) { + $info = $auth->getUserData($sub[0]); + if($info === false) continue; + $level = auth_aclcheck($id, $sub[0], $info['grps']); + if($level >= AUTH_READ) { + if(strcasecmp($info['mail'], $conf['notify']) != 0) { + $emails[$sub[0]] = $info['mail']; + } + } + } + } + $data['addresslist'] = trim($addresslist.','.implode(',', $emails), ','); + } + + } \ No newline at end of file -- cgit v1.2.3 From 835242b0f53a72a555ad30543a1677108ce210af Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 17:17:34 +0200 Subject: subscription system should work now again This readds the last part of the subscription system: the normal "every" subscriptions. --- inc/common.php | 2 +- inc/subscription.php | 57 ++++++++++++++++++++++------------------------------ 2 files changed, 25 insertions(+), 34 deletions(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index 29940d8a6..3cfeb3092 100644 --- a/inc/common.php +++ b/inc/common.php @@ -1122,7 +1122,7 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = $data = array('id' => $id, 'addresslist' => '', 'self' => false); trigger_event( 'COMMON_NOTIFY_ADDRESSLIST', $data, - 'subscription_addresslist' + array(new Subscription(), 'notifyaddresses') ); $bcc = $data['addresslist']; if(empty($bcc)) return false; diff --git a/inc/subscription.php b/inc/subscription.php index 804776ced..9b02225f9 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -12,7 +12,7 @@ * - subscription_lock * - subscription_unlock * - * @fixme handle $conf['subscribers'] and disable actions + * @fixme handle $conf['subscribers'] and disable actions and $auth == null * * @author Adrian Lang * @author Andreas Gohr @@ -480,26 +480,23 @@ class Subscription { return $mail->send(); } - - - // FIXME no refactoring below, yet - /** - * Return a string with the email addresses of all the - * users subscribed to a page + * Default callback for COMMON_NOTIFY_ADDRESSLIST * - * This is the default action for COMMON_NOTIFY_ADDRESSLIST. + * Aggregates all email addresses of user who have subscribed the given page with 'every' style * * @author Steven Danz * @author Adrian Lang * - * @todo this does NOT return a string but uses a reference to write back, either fix function or docs - * @param array $data Containing $id (the page id), $self (whether the author - * should be notified, $addresslist (current email address - * list) + * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead, + * use an array for the addresses within it + * + * @param array &$data Containing $id (the page id), $self (whether the author + * should be notified, $addresslist (current email address + * list) * @return string */ - function subscription_addresslist(&$data) { + public function notifyaddresses(&$data) { global $conf; /** @var auth_basic $auth */ global $auth; @@ -508,30 +505,24 @@ class Subscription { $self = $data['self']; $addresslist = $data['addresslist']; - if(!$conf['subscribers'] || $auth === null) { - return ''; - } - $pres = array('style' => 'every', 'escaped' => true); - if(!$self && isset($_SERVER['REMOTE_USER'])) { - $pres['user'] = '((?!'.preg_quote_cb($_SERVER['REMOTE_USER']). - '(?: |$))\S+)'; - } - $subs = subscription_find($id, $pres); - $emails = array(); - foreach($subs as $by_targets) { - foreach($by_targets as $sub) { - $info = $auth->getUserData($sub[0]); - if($info === false) continue; - $level = auth_aclcheck($id, $sub[0], $info['grps']); + $subscriptions = $this->subscribers($id, null, 'every'); + + $result = array(); + foreach($subscriptions as $target => $users) { + foreach($users as $user => $info) { + $userinfo = $auth->getUserData($user); + if($userinfo === false) continue; + if(!$userinfo['mail']) continue; + if(!$self && $user == $_SERVER['REMOTE_USER']) continue; //skip our own changes + + $level = auth_aclcheck($id, $user, $userinfo['grps']); if($level >= AUTH_READ) { - if(strcasecmp($info['mail'], $conf['notify']) != 0) { - $emails[$sub[0]] = $info['mail']; + if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere + $result[$user] = $userinfo['mail']; } } } } - $data['addresslist'] = trim($addresslist.','.implode(',', $emails), ','); + $data['addresslist'] = trim($addresslist.','.implode(',', $result), ','); } - - } \ No newline at end of file -- cgit v1.2.3 From 84c1127cc070777c8cbcf488f5422bc4b71470a8 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 17:30:01 +0200 Subject: correctly check if subscriptions are enabled --- inc/common.php | 2 +- inc/subscription.php | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index 3cfeb3092..b4a57b154 100644 --- a/inc/common.php +++ b/inc/common.php @@ -1117,7 +1117,7 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = $to = $conf['notify']; $bcc = ''; } elseif($who == 'subscribers') { - if(!$conf['subscribers']) return false; //subscribers enabled? + if(!actionOK('subscribe')) return false; //subscribers enabled? if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return false; //skip minors $data = array('id' => $id, 'addresslist' => '', 'self' => false); trigger_event( diff --git a/inc/subscription.php b/inc/subscription.php index 9b02225f9..742f5b1a0 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -21,6 +21,15 @@ class Subscription { + /** + * Check if subscription system is enabled + * + * @return bool + */ + public function isenabled() { + return actionOK('subscribe'); + } + /** * Return the subscription meta file for the given ID * @@ -156,6 +165,8 @@ class Subscription { * @return array */ public function subscribers($page, $user = null, $style = null, $data = null) { + if(!$this->isenabled()) return array(); + // Construct list of files which may contain relevant subscriptions. $files = array(':' => $this->file(':')); do { @@ -201,6 +212,8 @@ class Subscription { * @return bool */ public function add($id, $user, $style, $data = '') { + if(!$this->isenabled()) return false; + // delete any existing subscription $this->remove($id, $user); @@ -234,6 +247,8 @@ class Subscription { * @return bool */ public function remove($id, $user = null, $style = null, $data = null) { + if(!$this->isenabled()) return false; + $file = $this->file($id); if(!file_exists($file)) return true; @@ -254,10 +269,9 @@ class Subscription { * @author Adrian Lang */ function user_subscription($id = '', $user = '') { - global $ID; - global $conf; - if(!$conf['subscribers']) return false; + if(!$this->isenabled()) return false; + global $ID; if(!$id) $id = $ID; if(!$user) $user = $_SERVER['REMOTE_USER']; @@ -288,6 +302,8 @@ class Subscription { * @return int number of sent mails */ public function send_bulk($page) { + if(!$this->isenabled()) return 0; + /** @var auth_basic $auth */ global $auth; global $conf; @@ -497,9 +513,11 @@ class Subscription { * @return string */ public function notifyaddresses(&$data) { - global $conf; + if(!$this->isenabled()) return false; + /** @var auth_basic $auth */ global $auth; + global $conf; $id = $data['id']; $self = $data['self']; -- cgit v1.2.3 From a0519fdaeb59bd96d9071681a68c2dc1b646fc68 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 17:47:47 +0200 Subject: fixed subscription management now adding and removing subscriptions works again --- inc/actions.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'inc') diff --git a/inc/actions.php b/inc/actions.php index 62b0e1800..721492bd4 100644 --- a/inc/actions.php +++ b/inc/actions.php @@ -700,21 +700,28 @@ function act_subscription($act){ $target = $params['target']; $style = $params['style']; - $data = $params['data']; $action = $params['action']; // Perform action. - if (!subscription_set($_SERVER['REMOTE_USER'], $target, $style, $data)) { + $sub = new Subscription(); + if($action == 'unsubscribe'){ + $ok = $sub->remove($target, $_SERVER['REMOTE_USER'], $style); + }else{ + $ok = $sub->add($target, $_SERVER['REMOTE_USER'], $style); + } + + if($ok) { + msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']), + prettyprint_id($target)), 1); + act_redirect($ID, $act); + } else { throw new Exception(sprintf($lang["subscr_{$action}_error"], hsc($INFO['userinfo']['name']), prettyprint_id($target))); } - msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']), - prettyprint_id($target)), 1); - act_redirect($ID, $act); // Assure that we have valid data if act_redirect somehow fails. - $INFO['subscribed'] = get_info_subscribed(); + $INFO['subscribed'] = $sub->user_subscription(); return 'show'; } -- cgit v1.2.3 From 02308d17ed15cd633ba1fd5cab11994c42065121 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 18:01:30 +0200 Subject: initialize new subscriptions with current time We don't want to create a bunch of mails whenever a namespace is subscribed. Only changes *after* the subscription should be considered. This patch adds the timestamp to "every" style subscriptions as well, though this data is ignored. --- inc/subscription.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 742f5b1a0..800329cb5 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -223,6 +223,7 @@ class Subscription { if(!$user) throw new Exception('no subscription user given'); if(!$style) throw new Exception('no subscription style given'); + if(!$data) $data = time(); //always add current time for new subscriptions $line = "$user $style"; if($data) $line .= " $data"; -- cgit v1.2.3 From f036cff4fde59f0265f6123f6faf92cb8ba8bb26 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 12 Aug 2012 18:07:20 +0200 Subject: minor cleanup --- inc/subscription.php | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 800329cb5..74f4e4d03 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -1,24 +1,11 @@ * @author Andreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) */ - class Subscription { /** @@ -511,10 +498,9 @@ class Subscription { * @param array &$data Containing $id (the page id), $self (whether the author * should be notified, $addresslist (current email address * list) - * @return string */ public function notifyaddresses(&$data) { - if(!$this->isenabled()) return false; + if(!$this->isenabled()) return; /** @var auth_basic $auth */ global $auth; -- cgit v1.2.3 From 2ed38036a53a489d2fcadc46ce601f8c876fca31 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 21 Sep 2012 11:53:17 +0200 Subject: consolidate more notification code in subscription class This is untested and probably broken currently --- inc/auth.php | 11 +--- inc/common.php | 45 +++------------ inc/subscription.php | 159 +++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 126 insertions(+), 89 deletions(-) (limited to 'inc') diff --git a/inc/auth.php b/inc/auth.php index cedfdee36..3fb937613 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -778,23 +778,18 @@ function register() { return false; } - // create substitutions for use in notification email - $substitutions = array( - 'NEWUSER' => $_POST['login'], - 'NEWNAME' => $_POST['fullname'], - 'NEWEMAIL' => $_POST['email'], - ); + $subscription = new Subscription(); if(!$conf['autopasswd']) { msg($lang['regsuccess2'], 1); - notify('', 'register', '', $_POST['login'], false, $substitutions); + $subscription->send_register($_POST['login'], $_POST['fullname'], $_POST['email']); return true; } // autogenerated password? then send him the password if(auth_sendPassword($_POST['login'], $pass)) { msg($lang['regsuccess'], 1); - notify('', 'register', '', $_POST['login'], false, $substitutions); + $subscription->send_register($_POST['login'], $_POST['fullname'], $_POST['email']); return true; } else { msg($lang['regmailfail'], -1); diff --git a/inc/common.php b/inc/common.php index b4a57b154..d17061a1b 100644 --- a/inc/common.php +++ b/inc/common.php @@ -1113,9 +1113,8 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = // decide if there is something to do, eg. whom to mail if($who == 'admin') { if(empty($conf['notify'])) return false; //notify enabled? - $text = rawLocale('mailtext'); - $to = $conf['notify']; - $bcc = ''; + $tpl = 'mailtext'; + $to = $conf['notify']; } elseif($who == 'subscribers') { if(!actionOK('subscribe')) return false; //subscribers enabled? if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return false; //skip minors @@ -1124,57 +1123,29 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = 'COMMON_NOTIFY_ADDRESSLIST', $data, array(new Subscription(), 'notifyaddresses') ); - $bcc = $data['addresslist']; - if(empty($bcc)) return false; - $to = ''; - $text = rawLocale('subscr_single'); + $to = $data['addresslist']; + if(empty($to)) return false; + $tpl = 'subscr_single'; } elseif($who == 'register') { if(empty($conf['registernotify'])) return false; $text = rawLocale('registermail'); $to = $conf['registernotify']; - $bcc = ''; } else { return false; //just to be safe } - // prepare replacements (keys not set in hrep will be taken from trep) - $trep = array( - 'NEWPAGE' => wl($id, '', true, '&'), - 'PAGE' => $id, - 'SUMMARY' => $summary - ); - $trep = array_merge($trep, $replace); - $hrep = array(); - // prepare content if($who == 'register') { $subject = $lang['mail_new_user'].' '.$summary; - } elseif($rev) { - $subject = $lang['mail_changed'].' '.$id; - $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); - $df = new Diff(explode("\n", rawWiki($id, $rev)), - explode("\n", rawWiki($id))); - $dformat = new UnifiedDiffFormatter(); - $tdiff = $dformat->format($df); - - $DIFF_INLINESTYLES = true; - $dformat = new InlineDiffFormatter(); - $hdiff = $dformat->format($df); - $hdiff = ''.$hdiff.'
'; - $DIFF_INLINESTYLES = false; } else { - $subject = $lang['mail_newpage'].' '.$id; - $trep['OLDPAGE'] = '---'; - $tdiff = rawWiki($id); - $hdiff = nl2br(hsc($tdiff)); + $subscription = new Subscription(); + return $subscription->send_diff($to, $tpl, $id, $rev, $summary); } - $trep['DIFF'] = $tdiff; - $hrep['DIFF'] = $hdiff; + // send mail $mail = new Mailer(); $mail->to($to); - $mail->bcc($bcc); $mail->subject($subject); $mail->setBody($text, $trep, $hrep); if($who == 'subscribers') { diff --git a/inc/subscription.php b/inc/subscription.php index 74f4e4d03..4757b216c 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -377,16 +377,90 @@ class Subscription { return $count; } + /** + * Send the diff for some page change + * + * @param string $subscriber_mail The target mail address + * @param string $template Mail template, should be 'subscr_digest' or 'subscr_single' + * @param string $id Page for which the notification is + * @param int|null $rev Old revision if any + * @param string $summary Change summary if any + * @return bool true if successfully sent + */ + public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '') { + global $DIFF_INLINESTYLES; + + // prepare replacements (keys not set in hrep will be taken from trep) + $trep = array( + 'PAGE' => $id, + 'NEWPAGE' => wl($id, '', true, '&'), + 'SUMMARY' => $summary, + 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') + ); + $hrep = array(); + + if($rev) { + $subject = 'changed'; + $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); + $df = new Diff(explode("\n", rawWiki($id, $rev)), + explode("\n", rawWiki($id))); + $dformat = new UnifiedDiffFormatter(); + $tdiff = $dformat->format($df); + + $DIFF_INLINESTYLES = true; + $dformat = new InlineDiffFormatter(); + $hdiff = $dformat->format($df); + $hdiff = ''.$hdiff.'
'; + $DIFF_INLINESTYLES = false; + + } else { + $subject = 'newpage'; + $trep['OLDPAGE'] = '---'; + $tdiff = rawWiki($id); + $hdiff = nl2br(hsc($tdiff)); + } + + $trep['DIFF'] = $tdiff; + $hrep['DIFF'] = $hdiff; + + return $this->send( + $subscriber_mail, $subject, $id, + $template, $trep, $hrep + ); + } + + public function send_register($login, $fullname, $email) { + global $conf; + global $ID; + if(empty($conf['registernotify'])) return false; + + $trep = array( + 'NEWUSER' => $login, + 'NEWNAME' => $fullname, + 'NEWEMAIL' => $email, + ); + + return $this->send( + $conf['registernotify'], + 'new_user', + $ID, + 'registermail', + $trep + ); + } + /** * Send a digest mail * - * Sends a digest mail showing a bunch of changes. + * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff() + * but determines the last known revision first * * @author Adrian Lang * * @param string $subscriber_mail The target mail address * @param array $id The ID * @param int $lastupdate Time of the last notification + * @return bool */ protected function send_digest($subscriber_mail, $id, $lastupdate) { $n = 0; @@ -395,25 +469,10 @@ class Subscription { $rev = (count($rev) > 0) ? $rev[0] : null; } while(!is_null($rev) && $rev > $lastupdate); - $replaces = array( - 'NEWPAGE' => wl($id, '', true, '&'), - 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') - ); - if(!is_null($rev)) { - $subject = 'changed'; - $replaces['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); - $df = new Diff(explode("\n", rawWiki($id, $rev)), - explode("\n", rawWiki($id))); - $dformat = new UnifiedDiffFormatter(); - $replaces['DIFF'] = $dformat->format($df); - } else { - $subject = 'newpage'; - $replaces['OLDPAGE'] = 'none'; - $replaces['DIFF'] = rawWiki($id); - } - $this->send( - $subscriber_mail, $replaces, $subject, $id, - 'subscr_digest' + return $this->send_diff( + $subscriber_mail, + 'subscr_digest', + $id, $rev ); } @@ -427,27 +486,35 @@ class Subscription { * @param string $subscriber_mail The target mail address * @param array $ids Array of ids * @param string $ns_id The id of the namespace + * @return bool */ protected function send_list($subscriber_mail, $ids, $ns_id) { if(count($ids) === 0) return; - global $conf; - $list = ''; + + $tlist = ''; + $hlist = '
    '; foreach($ids as $id) { - $list .= '* '.wl($id, array(), true).NL; + $link = wl($id, array(), true); + $tlist .= '* '.$link.NL; + $hlist .= '
  • '.hsc($id).'
  • '.NL; } - $this->send( + $hlist = '
'; + + $id = prettyprint_id($ns_id); + $trep = array( + 'DIFF' => rtrim($tlist), + 'PAGE' => $id, + 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') + ); + $hrep = array( + 'DIFF' => $hlist + ); + + return $this->send( $subscriber_mail, - array( - 'DIFF' => rtrim($list), - 'SUBSCRIBE' => wl( - $ns_id.$conf['start'], - array('do' => 'subscribe'), - true, '&' - ) - ), 'subscribe_list', - prettyprint_id($ns_id), - 'subscr_list' + + 'subscr_list', $trep, $hrep ); } @@ -457,30 +524,34 @@ class Subscription { * @author Adrian Lang * * @param string $subscriber_mail The target mail address - * @param array $replaces Predefined parameters used to parse the - * template * @param string $subject The lang id of the mail subject (without the * prefix “mail_”) * @param string $id The page or namespace id * @param string $template The name of the mail template + * @param array $trep Predefined parameters used to parse the + * template (in text format) + * @param array $hrep Predefined parameters used to parse the + * template (in HTML format), null to default to $trep * @return bool */ - protected function send($subscriber_mail, $replaces, $subject, $id, $template) { + protected function send($subscriber_mail, $subject, $id, $template, $trep, $hrep = null) { global $lang; $text = rawLocale($template); - $trep = array_merge($replaces, array('PAGE' => $id)); + $trep = array_merge( + $trep, array( + + ) + ); $subject = $lang['mail_'.$subject].' '.$id; $mail = new Mailer(); $mail->bcc($subscriber_mail); $mail->subject($subject); - $mail->setBody($text, $trep); - $mail->setHeader( - 'List-Unsubscribe', - '<'.wl($id, array('do'=> 'subscribe'), true, '&').'>', - false - ); + $mail->setBody($text, $trep, $hrep); + if(isset($trep['SUBSCRIBE'])){ + $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); + } return $mail->send(); } -- cgit v1.2.3 From bee9f377bc547c99fe99b4e38199cb92cf668554 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 3 Nov 2012 17:54:02 +0100 Subject: Completely rewritten Tar library This new class is only losely based on our previous library. The whole API was changed to make it more flexible and memory saving. Some fisrt unit tests are included --- inc/Tar.class.php | 614 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ inc/load.php | 1 + 2 files changed, 615 insertions(+) create mode 100644 inc/Tar.class.php (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php new file mode 100644 index 000000000..7f5e5af4a --- /dev/null +++ b/inc/Tar.class.php @@ -0,0 +1,614 @@ +open('myfile.tgz'); + * $toc = $tar->contents(); + * print_r($toc); + * + * To extract the contents of an existing TAR archive, open() it and use extract() on it: + * + * $tar = new Tar(); + * $tar->open('myfile.tgz'); + * $tar->extract(/tmp); + * + * To create a new TAR archive directly on the filesystem (low memory requirements), create() it, + * add*() files and close() it: + * + * $tar = new Tar(); + * $tar->create('myfile.tgz'); + * $tar->addFile(...); + * $tar->addData(...); + * ... + * $tar->close(); + * + * To create a TAR archive directly in memory, create() it, add*() files and then either save() + * or getData() it: + * + * $tar = new Tar(); + * $tar->create(); + * $tar->addFile(...); + * $tar->addData(...); + * ... + * $tar->save('myfile.tgz'); // compresses and saves it + * echo $tar->getArchive(Tar::COMPRESS_GZIP); // compresses and returns it + * + * @author Andreas Gohr + * @author Bouchon (Maxg) + * @license GPL 2 + */ +class Tar { + + const COMPRESS_AUTO = 0; + const COMPRESS_NONE = 1; + const COMPRESS_GZIP = 2; + const COMPRESS_BZIP = 3; + + protected $file = ''; + protected $comptype = Tar::COMPRESS_AUTO; + protected $fh; + protected $memory = ''; + protected $closed = true; + protected $writeaccess = false; + + /** + * Open an existing TAR file for reading + * + * @param string $file + * @param int $comptype + * @throws TarIOException + */ + public function open($file, $comptype = Tar::COMPRESS_AUTO) { + // determine compression + if($comptype == Tar::COMPRESS_AUTO) $comptype = $this->filetype($file); + $this->compressioncheck($comptype); + + $this->comptype = $comptype; + $this->file = $file; + + if($this->comptype === Tar::COMPRESS_GZIP) { + $this->fh = @gzopen($this->file, 'rb'); + } elseif($this->comptype === Tar::COMPRESS_BZIP) { + $this->fh = @bzopen($this->file, 'r'); + } else { + $this->fh = @fopen($this->file, 'rb'); + } + + if(!$this->fh) throw(new TarIOException('Could not open file for reading: '.$this->file)); + $this->closed = false; + } + + /** + * Read the contents of a TAR archive + * + * This function lists the files stored in the archive, and returns an indexed array of associative + * arrays containing for each file the following information: + * + * checksum Tar Checksum of the file + * filename The full name of the stored file (up to 100 c.) + * mode UNIX permissions in DECIMAL, not octal + * uid The Owner ID + * gid The Group ID + * size Uncompressed filesize + * mtime Timestamp of last modification + * typeflag Empty for files, set for folders + * link Is it a symlink? + * uname Owner name + * gname Group name + * + * The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams. + * Reopen the file with open() again if you want to do additional operations + */ + public function contents() { + if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive')); + + $result = Array(); + while($read = $this->readbytes(512)) { + $header = $this->parseHeader($read); + if(!is_array($header)) continue; + + $this->skipbytes(ceil($header['size'] / 512) * 512, 1); + $result[] = $header; + } + + $this->close(); + return $result; + } + + /** + * Extract an existing TAR archive + * + * The $strip parameter allows you to strip a certain number of path components from the filenames + * found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when + * an integer is passed as $strip. + * Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix, + * the prefix will be stripped. It is recommended to give prefixes with a trailing slash. + * + * By default this will extract all files found in the archive. You can restrict the output using the $include + * and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If + * $include is set only files that match this expression will be extracted. Files that match the $exclude + * expression will never be extracted. Both parameters can be used in combination. Expressions are matched against + * stripped filenames as described above. + * + * The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams. + * Reopen the file with open() again if you want to do additional operations + * + * @param string $outdir the target directory for extracting + * @param int|string $strip either the number of path components or a fixed prefix to strip + * @param string $exclude a regular expression of files to exclude + * @param string $include a regular expression of files to include + * @throws TarIOException + * @return array + */ + function extract($outdir, $strip='', $exclude='', $include='') { + if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive')); + + $outdir = rtrim($outdir,'/'); + io_mkdir_p($outdir); + $striplen = strlen($strip); + + $extracted = array(); + + while($dat = $this->readbytes(512)) { + // read the file header + $header = $this->parseHeader($dat); + if(!is_array($header)) continue; + if(!$header['filename']) continue; + + // strip prefix + $filename = $this->cleanPath($header['filename']); + if(is_int($strip)) { + // if $strip is an integer we strip this many path components + $parts = explode('/',$filename); + if(!$header['typeflag']){ + $base = array_pop($parts); // keep filename itself + }else{ + $base = ''; + } + $filename = join('/',array_slice($parts,$strip)); + if($base) $filename .= "/$base"; + }else{ + // ifstrip is a string, we strip a prefix here + if(substr($filename,0,$striplen) == $strip) $filename = substr($filename,$striplen); + } + + // check if this should be extracted + $extract = true; + if(!$filename){ + $extract = false; + }else{ + if($include){ + if(preg_match($include, $filename)){ + $extract = true; + }else{ + $extract = false; + } + } + if($exclude && preg_match($exclude, $filename)){ + $extract = false; + } + } + + // Now do the extraction (or not) + if($extract) { + $extracted[] = $header; + + $output = "$outdir/$filename"; + $directory = ($header['typeflag']) ? $output : dirname($output); + io_mkdir_p($directory); + + // is this a file? + if(!$header['typeflag']){ + $fp = @fopen($output, "wb"); + if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output)); + + $size = floor($header['size'] / 512); + for($i = 0; $i < $size; $i++) { + fwrite($fp, $this->readbytes(512), 512); + } + if(($header['size'] % 512) != 0) fwrite($fp, $this->readbytes(512), $header['size'] % 512); + + fclose($fp); + touch($output, $header['mtime']); + chmod($output, $header['perm']); + }else{ + $this->skipbytes(ceil($header['size'] / 512) * 512); // the size is usually 0 for directories + } + }else{ + $this->skipbytes(ceil($header['size'] / 512) * 512); + } + } + + $this->close(); + return $extracted; + } + + /** + * Create a new TAR file + * + * If $file is empty, the tar file will be created in memory + * + * @param string $file + * @param int $comptype + * @param int $complevel + * @throws TarIOException + * @throws TarIllegalCompressionException + */ + public function create($file = '', $comptype = Tar::COMPRESS_AUTO, $complevel = 9) { + // determine compression + if($comptype == Tar::COMPRESS_AUTO) $comptype = $this->filetype($file); + $this->compressioncheck($comptype); + + $this->comptype = $comptype; + $this->file = $file; + $this->memory = ''; + $this->fh = 0; + + if($this->file) { + if($this->comptype === Tar::COMPRESS_GZIP) { + $this->fh = @gzopen($this->file, 'wb'.$complevel); + } elseif($this->comptype === Tar::COMPRESS_BZIP) { + $this->fh = @bzopen($this->file, 'w'); + } else { + $this->fh = @fopen($this->file, 'wb'); + } + + if(!$this->fh) throw(new TarIOException('Could not open file for writing: '.$this->file)); + } + $this->writeaccess = false; + $this->closed = false; + } + + /** + * Add a file to the current TAR archive using an existing file in the filesystem + * + * @todo handle directory adding + * @param string $file the original file + * @param string $name the name to use for the file in the archive + * @throws TarBadFilename + * @throws TarIOException + */ + public function addFile($file, $name = '') { + if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added')); + + if(!$name) $name = $file; + $name = $this->cleanPath($name); + + // FIXME ustar should support up 256 chars + if(strlen($name) > 99) throw(new TarBadFilename('Filenames may not exceed 99 bytes: '.$name)); + + $fp = fopen($file, 'rb'); + if(!$fp) throw(new TarIOException('Could not open file for reading: '.$file)); + + // create file header and copy all stat info from the original file + clearstatcache(false, $file); + $stat = stat($file); + $this->writeFileHeader( + $name, + $stat[4], + $stat[5], + fileperms($file), + filesize($file), + filemtime($file), + false + ); + + while(!feof($fp)) { + $packed = pack("a512", fread($fp, 512)); + $this->writebytes($packed); + } + fclose($fp); + } + + /** + * Add a file to the current TAR archive using in memory data + * + * @param $name + * @param $data + * @param int $uid + * @param int $gid + * @param int $perm + * @param int $mtime + * @throws TarIOException + * @throws TarBadFilename + */ + public function addData($name, $data, $uid = 0, $gid = 0, $perm = 0666, $mtime = 0) { + if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added')); + + $name = $this->cleanPath($name); + + // FIXME ustar should support up 256 chars + if(strlen($name) > 99) throw(new TarBadFilename('Filenames may not exceed 99 bytes: '.$name)); + + $len = strlen($data); + + $this->writeFileHeader( + $name, + $uid, + $gid, + $perm, + $len, + ($mtime) ? $mtime : time(), + false + ); + + for($s = 0; $s < $len; $s += 512) { + $this->writebytes(pack("a512", substr($data, $s, 512))); + } + } + + /** + * Add the closing footer to the archive if in write mode, close all file handles + * + * After a call to this function no more data can be added to the archive, for + * read access no reading is allowed anymore + * + * "Physically, an archive consists of a series of file entries terminated by an end-of-archive entry, which + * consists of two 512 blocks of zero bytes" + * + * @link http://www.gnu.org/software/tar/manual/html_chapter/tar_8.html#SEC134 + */ + public function close() { + if($this->closed) return; // we did this already + + // write footer + if($this->writeaccess){ + $this->writebytes(pack("a512", "")); + $this->writebytes(pack("a512", "")); + } + + // close file handles + if($this->file){ + if($this->comptype === Tar::COMPRESS_GZIP){ + gzclose($this->fh); + }elseif($this->comptype === Tar::COMPRESS_BZIP){ + bzclose($this->fh); + }else{ + fclose($this->fh); + } + + $this->file = ''; + $this->fh = 0; + } + + $this->closed = true; + } + + /** + * Returns the created in-memory archive data + * + * This implicitly calls close() on the Archive + */ + public function getArchive($comptype = Tar::COMPRESS_AUTO, $complevel = 9) { + $this->close(); + + if($comptype === Tar::COMPRESS_AUTO) $comptype = $this->comptype; + $this->compressioncheck($comptype); + + if($comptype === Tar::COMPRESS_GZIP) return gzcompress($this->memory, $complevel); + if($comptype === Tar::COMPRESS_BZIP) return bzcompress($this->memory); + return $this->memory; + } + + /** + * Save the created in-memory archive data + * + * Note: It more memory effective to specify the filename in the create() function and + * let the library work on the new file directly. + * + * @param $file + * @param int $comptype + * @param int $complevel + * @throws TarIOException + */ + public function save($file, $comptype = Tar::COMPRESS_AUTO, $complevel = 9) { + if($comptype === Tar::COMPRESS_AUTO) $comptype = $this->filetype($file); + + if(!file_put_contents($file, $this->getArchive($comptype, $complevel))) { + throw(new TarIOException('Could not write to file: '.$file)); + } + } + + /** + * Read from the open file pointer + * + * @param int $length bytes to read + * @return string + */ + protected function readbytes($length) { + if($this->comptype === Tar::COMPRESS_GZIP) { + return @gzread($this->fh, $length); + } elseif($this->comptype === Tar::COMPRESS_BZIP) { + return @bzread($this->fh, $length); + } else { + return @fread($this->fh, $length); + } + } + + /** + * Write to the open filepointer or memory + * + * @param string $data + * @throws TarIOException + * @return int number of bytes written + */ + protected function writebytes($data) { + if(!$this->file) { + $this->memory .= $data; + $written = strlen($data); + } elseif($this->comptype === Tar::COMPRESS_GZIP) { + $written = @gzwrite($this->fh, $data); + } elseif($this->comptype === Tar::COMPRESS_BZIP) { + $written = @bzwrite($this->fh, $data); + } else { + $written = @fwrite($this->fh, $data); + } + if($written === false) throw(new TarIOException('Failed to write to archive stream')); + return $written; + } + + /** + * Skip forward in the open file pointer + * + * This is basically a wrapper around seek() (and a workarounf for bzip2) + * + * @param int $bytes seek to this position + */ + function skipbytes($bytes) { + if($this->comptype === Tar::COMPRESS_GZIP){ + @gzseek($this->fh, $bytes, SEEK_CUR); + }elseif($this->comptype === Tar::COMPRESS_BZIP){ + // there is no seek in bzip2, we simply read on + @bzread($this->fh, $bytes); + }else{ + @fseek($this->fh, $bytes, SEEK_CUR); + } + } + + /** + * Write a file header + * + * @param string $name + * @param int $uid + * @param int $gid + * @param int $perm + * @param int $size + * @param int $mtime + * @param bool $isdir + */ + protected function writeFileHeader($name, $uid, $gid, $perm, $size, $mtime, $isdir = false) { + // values are needed in octal + $uid = sprintf("%6s ", DecOct($uid)); + $gid = sprintf("%6s ", DecOct($gid)); + $perm = sprintf("%6s ", DecOct($perm)); + $size = sprintf("%11s ", DecOct($size)); + $mtime = sprintf("%11s", DecOct($mtime)); + $dir = ($isdir) ? '5' : ''; + + $data_first = pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime); + $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', ""); + + for($i = 0, $chks = 0; $i < 148; $i++) + $chks += ord($data_first[$i]); + + for($i = 156, $chks += 256, $j = 0; $i < 512; $i++, $j++) + $chks += ord($data_last[$j]); + + $this->writebytes($data_first); + + $chks = pack("a8", sprintf("%6s ", DecOct($chks))); + $this->writebytes($chks.$data_last); + } + + /** + * Decode the given tar file header + * + * @todo how to handle filenames >100 chars? + * @param string $block a 512 byte block containign the header data + * @return array|bool + */ + protected function parseHeader($block) { + if(!$block || strlen($block) != 512) return false; + + for($i = 0, $chks = 0; $i < 148; $i++) + $chks += ord($block[$i]); + + for($i = 156, $chks += 256; $i < 512; $i++) + $chks += ord($block[$i]); + + $headers = @unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $block); + if(!$headers) return false; + + $return['checksum'] = OctDec(trim($headers['checksum'])); + if($return['checksum'] != $chks) return false; + + $return['filename'] = trim($headers['filename']); + $return['perm'] = OctDec(trim($headers['perm'])); + $return['uid'] = OctDec(trim($headers['uid'])); + $return['gid'] = OctDec(trim($headers['gid'])); + $return['size'] = OctDec(trim($headers['size'])); + $return['mtime'] = OctDec(trim($headers['mtime'])); + $return['typeflag'] = $headers['typeflag']; + $return['link'] = trim($headers['link']); + $return['uname'] = trim($headers['uname']); + $return['gname'] = trim($headers['gname']); + + return $return; + } + + /** + * Cleans up a path and removes relative parts + * + * @param string $p_dir + * @return string + */ + protected function cleanPath($p_dir) { + $r = ''; + if($p_dir) { + $subf = explode("/", $p_dir); + + for($i = count($subf) - 1; $i >= 0; $i--) { + if($subf[$i] == ".") { + # do nothing + } elseif($subf[$i] == "..") { + $i--; + } elseif(!$subf[$i] && $i != count($subf) - 1 && $i) { + # do nothing + } else { + $r = $subf[$i].($i != (count($subf) - 1) ? "/".$r : ""); + } + } + } + return $r; + } + + /** + * Checks if the given compression type is available and throws an exception if not + * + * @param $comptype + * @throws TarIllegalCompressionException + */ + protected function compressioncheck($comptype) { + if($comptype === Tar::COMPRESS_GZIP && !function_exists('gzopen')) { + throw(new TarIllegalCompressionException('No gzip support available')); + } + + if($comptype === Tar::COMPRESS_BZIP && !function_exists('bzopen')) { + throw(new TarIllegalCompressionException('No bzip2 support available')); + } + } + + /** + * Guesses the wanted compression from the given filename extension + * + * You don't need to call this yourself. It's used when you pass Tar::COMPRESS_AUTO somewhere + * + * @param string $file + * @return int + */ + public function filetype($file) { + $file = strtolower($file); + if(substr($file, -3) == '.gz' || substr($file, -4) == '.tgz') { + $comptype = Tar::COMPRESS_GZIP; + } elseif(substr($file, -4) == '.bz2' || substr($file, -4) == '.tbz') { + $comptype = Tar::COMPRESS_BZIP; + } else { + $comptype = Tar::COMPRESS_NONE; + } + return $comptype; + } +} + +class TarBadFilename extends Exception { +} + +class TarIOException extends Exception { +} + +class TarIllegalCompressionException extends Exception { +} \ No newline at end of file diff --git a/inc/load.php b/inc/load.php index b8a279523..49c307054 100644 --- a/inc/load.php +++ b/inc/load.php @@ -71,6 +71,7 @@ function load_autoload($name){ 'IXR_IntrospectionServer' => DOKU_INC.'inc/IXR_Library.php', 'Doku_Plugin_Controller'=> DOKU_INC.'inc/plugincontroller.class.php', 'GeSHi' => DOKU_INC.'inc/geshi.php', + 'Tar' => DOKU_INC.'inc/Tar.class.php', 'TarLib' => DOKU_INC.'inc/TarLib.class.php', 'ZipLib' => DOKU_INC.'inc/ZipLib.class.php', 'DokuWikiFeedCreator' => DOKU_INC.'inc/feedcreator.class.php', -- cgit v1.2.3 From fba11f64c26a8dde5f64c57233b0da84fae35959 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 3 Nov 2012 18:42:57 +0100 Subject: removed old TarLib and changed plugin manager to use new one --- inc/TarLib.class.php | 921 --------------------------------------------------- inc/load.php | 1 - 2 files changed, 922 deletions(-) delete mode 100644 inc/TarLib.class.php (limited to 'inc') diff --git a/inc/TarLib.class.php b/inc/TarLib.class.php deleted file mode 100644 index e04c47cb8..000000000 --- a/inc/TarLib.class.php +++ /dev/null @@ -1,921 +0,0 @@ - (Maxg) - * @author Christopher Smith - */ - -/** - * Those constants represent the compression method to use. - * COMPRESS_GZIP is used for the GZIP compression; COMPRESS_BZIP for - * BZIP2 and COMPRESS_NONE for no compression. - * - * On the other hand, COMPRESS_AUTO is a bit harder. It will first check - * if the zlib extensions are loaded. - * If it is, GZIP will be used. Else it will check if the bz2 extensions - * are loaded. If it is, BZIP2 will be used. Else no compression will be - * performed. - * - * You can then use getCompression() to know the compression chosen. - * - * If you selected a compression which can't be used (i.e extension not - * present), it will be just disabled, and won't produce any error ! - * As a consequence, getCompression() will return COMPRESS_NONE - * - * ARCHIVE_DYNAMIC can be passed as the first argument of the constructor, to - * create an archive in memory instead of a file. See also: MaxgTar(), - * getDynamicArchive() and writeArchive() - * - * ARCHIVE_RENAMECOMP is a flag that can be multiplied by the compression method - * (i.e COMPRESS_AUTO * ARCHIVE_RENAMECOMP). This will add the correct extension - * to the archive name, which is useful with COMPRESS_AUTO, ie .bz2 if you gave - * COMPRESS_BZIP. See also getCompression(TRUE) which does exactly the - * same - * - * COMPRESS_DETECT does exactly the opposite and try to detect the - * compression to use to read the archive depending on its extension. (i.e if - * the archive ends with .tar.gz TarLib will try to decompress it with - * GZIP). See also setCompression() - * - * FULL_ARCHIVE is a -1 constant that means "the complete archive" when - * extracting. This is explained in Extract() - */ -#define('COMPRESS_GZIP',1); -#define('COMPRESS_BZIP',2); -#define('COMPRESS_AUTO',3); -#define('COMPRESS_NONE',0); -#define('TARLIB_VERSION','1.2'); -#define('FULL_ARCHIVE',-1); -#define('ARCHIVE_DYNAMIC',0); -#define('ARCHIVE_RENAMECOMP',5); -#define('COMPRESS_DETECT',-1); - -class TarLib { - var $_comptype; - var $_compzlevel; - var $_fp; - var $_memdat; - var $_nomf; - var $_result; - var $_initerror; - - const COMPRESS_GZIP = 1; - const COMPRESS_BZIP = 2; - const COMPRESS_AUTO = 3; - const COMPRESS_NONE = 0; - const TARLIB_VERSION = '1.2'; - const FULL_ARCHIVE = -1; - const ARCHIVE_DYNAMIC = 0; - const ARCHIVE_RENAMECOMP = 5; - const COMPRESS_DETECT = -1; - - /** - * constructor, initialize the class - * - * The constructor initialize the variables and prepare the class for the - * archive, and return the object created. Note that you can use multiple - * instances of the MaxgTar class, if you call this function another time and - * store the object in an other variable. - * - * In fact, MaxgTar accepts the following arguments (all are optional) : - * - * filename can be either a file name (absolute or relative). In this - * case, it can be used both for reading and writing. You can also open - * remote archive if you add a protocole name at the beginning of the file - * (ie https://host.dom/archive.tar.gz), but for reading only and if the - * directive allow_url_fopen is enabled in PHP.INI (this can be checked with - * TarInfo()). If you pass a file that doesn't exist, the script - * will try to create it. If the archive already exists and contains files, - * you can use Add() to append files.But by default this parameter - * is ARCHIVE_DYNAMIC (write only) so the archive is created in memory and - * can be sent to a file [writeArchive()] or to the client - * [sendClient()] - * - * compression_type should be a constant that represents a type of - * compression, or its integer value. The different values are described in - * the constants. - * - * compression_level is an integer between 1 and 9 (by default) an - * represent the GZIP or BZIP compression level. 1 produce fast compression, - * and 9 produce smaller files. See the RFC 1952 for more infos. - */ - function __construct($p_filen = TarLib::ARCHIVE_DYNAMIC, $p_comptype = TarLib::COMPRESS_AUTO, $p_complevel = 9) { - $this->_initerror = 0; - $this->_nomf = $p_filen; - $flag = 0; - if($p_comptype && $p_comptype % 5 == 0) { - $p_comptype /= TarLib::ARCHIVE_RENAMECOMP; - $flag = 1; - } - - if($p_complevel > 0 && $p_complevel <= 9) $this->_compzlevel = $p_complevel; - else $this->_compzlevel = 9; - - if($p_comptype == TarLib::COMPRESS_DETECT) { - if(strtolower(substr($p_filen, -3)) == '.gz') $p_comptype = TarLib::COMPRESS_GZIP; - elseif(strtolower(substr($p_filen, -4)) == '.bz2') $p_comptype = TarLib::COMPRESS_BZIP; - else $p_comptype = TarLib::COMPRESS_NONE; - } - - switch($p_comptype) { - case TarLib::COMPRESS_GZIP: - if(!extension_loaded('zlib')) $this->_initerror = -1; - $this->_comptype = TarLib::COMPRESS_GZIP; - break; - - case TarLib::COMPRESS_BZIP: - if(!extension_loaded('bz2')) $this->_initerror = -2; - $this->_comptype = TarLib::COMPRESS_BZIP; - break; - - case TarLib::COMPRESS_AUTO: - if(extension_loaded('zlib')) - $this->_comptype = TarLib::COMPRESS_GZIP; - elseif(extension_loaded('bz2')) - $this->_comptype = TarLib::COMPRESS_BZIP; - else - $this->_comptype = TarLib::COMPRESS_NONE; - break; - - default: - $this->_comptype = TarLib::COMPRESS_NONE; - } - - if($this->_initerror < 0) $this->_comptype = TarLib::COMPRESS_NONE; - - if($flag) $this->_nomf .= '.'.$this->getCompression(1); - $this->_result = true; - } - - /** - * Recycle a TAR object. - * - * This function does exactly the same as TarLib (constructor), except it - * returns a status code. - */ - function setArchive($p_name = '', $p_comp = TarLib::COMPRESS_AUTO, $p_level = 9) { - $this->_CompTar(); - $this->__construct($p_name, $p_comp, $p_level); - return $this->_result; - } - - /** - * Get the compression used to generate the archive - * - * This is a very useful function when you're using dynamical archives. - * Besides, if you let the script chose which compression to use, you'll have - * a problem when you'll want to send it to the client if you don't know - * which compression was used. - * - * There are two ways to call this function : if you call it without argument - * or with FALSE, it will return the compression constants, explained on the - * MaxgTar Constants. If you call it with GetExtension on TRUE it will - * return the extension without starting dot (ie "tar" or "tar.bz2" or - * "tar.gz") - * - * NOTE: This can be done with the flag ARCHIVE_RENAMECOMP, see the - * MaxgTar Constants - */ - function getCompression($ext = false) { - $exts = Array('tar', 'tar.gz', 'tar.bz2'); - if($ext) return $exts[$this->_comptype]; - return $this->_comptype; - } - - /** - * Change the compression mode. - * - * This function will change the compression methode to read or write - * the archive. See the MaxgTar Constants to see which constants you can use. - * It may look strange, but it returns the GZIP compression level. - */ - function setCompression($p_comp = TarLib::COMPRESS_AUTO) { - $this->setArchive($this->_nomf, $p_comp, $this->_compzlevel); - return $this->_compzlevel; - } - - /** - * Returns the compressed dynamic archive. - * - * When you're working with dynamic archives, use this function to grab - * the final compressed archive in a string ready to be put in a SQL table or - * in a file. - */ - function getDynamicArchive() { - return $this->_encode($this->_memdat); - } - - /** - * Write a dynamical archive into a file - * - * This function attempts to write a dynamicaly-genrated archive into - * TargetFile on the webserver. It returns a TarErrorStr() status - * code. - * - * To know the extension to add to the file if you're using AUTO_DETECT - * compression, you can use getCompression(). - */ - function writeArchive($p_archive) { - if(!$this->_memdat) return -7; - $fp = @fopen($p_archive, 'wb'); - if(!$fp) return -6; - - fwrite($fp, $this->_memdat); - fclose($fp); - - return true; - } - - /** - * Send a TAR archive to the client browser. - * - * This function will send an archive to the client, and return a status - * code, but can behave differently depending on the arguments you give. All - * arguments are optional. - * - * ClientName is used to specify the archive name to give to the browser. If - * you don't give one, it will send the constructor filename or return an - * error code in case of dynamical archive. - * - * FileName is optional and used to send a specific archive. Leave it blank - * to send dynamical archives or the current working archive. - * - * If SendHeaders is enabled (by default), the library will send the HTTP - * headers itself before it sends the contents. This headers are : - * Content-Type, Content-Disposition, Content-Length and Accept-Range. - * - * Please note that this function DOES NOT stops the script so don't forget - * to exit() to avoid your script sending other data and corrupt the archive. - * Another note : for AUTO_DETECT dynamical archives you can know the - * extension to add to the name with getCompression() - */ - function sendClient($name = '', $archive = '', $headers = true) { - if(!$name && !$this->_nomf) return -9; - if(!$archive && !$this->_memdat) return -10; - if(!$name) $name = utf8_basename($this->_nomf); - - if($archive) { - if(!file_exists($archive)) return -11; - } - $decoded = $this->getDynamicArchive(); - - if($headers) { - header('Content-Type: application/x-gtar'); - header('Content-Disposition: attachment; filename='.utf8_basename($name)); - header('Accept-Ranges: bytes'); - header('Content-Length: '.($archive ? filesize($archive) : strlen($decoded))); - } - - if($archive) { - $fp = @fopen($archive, 'rb'); - if(!$fp) return -4; - - while(!feof($fp)) echo fread($fp, 2048); - } else { - echo $decoded; - } - - return true; - } - - /** - * Extract part or totality of the archive. - * - * This function can extract files from an archive, and returns then a - * status codes that can be converted with TarErrorStr() into a - * human readable message. - * - * Only the first argument is required, What and it can be either the - * constant FULL_ARCHIVE or an indexed array containing each file you want to - * extract. - * - * To contains the target folder to extract the archive. It is optional and - * the default value is '.' which means the current folder. If the target - * folder doesn't exist, the script attempts to create it and give it - * permissions 0777 by default. - * - * RemovePath is very usefull when you want to extract files from a subfoler - * in the archive to a root folder. For instance, if you have a file in the - * archive called some/sub/folder/test.txt and you want to extract it to the - * script folder, you can call Extract with To = '.' and RemovePath = - * 'some/sub/folder/' - * - * FileMode is optional and its default value is 0755. It is in fact the UNIX - * permission in octal mode (prefixed with a 0) that will be given on each - * extracted file. - */ - function Extract($p_what = TarLib::FULL_ARCHIVE, $p_to = '.', $p_remdir = '', $p_mode = 0755) { - if(!$this->_OpenRead()) return -4; - // if(!@is_dir($p_to)) if(!@mkdir($p_to, 0777)) return -8; --CS - if(!@is_dir($p_to)) if(!$this->_dirApp($p_to)) return -8; //--CS (route through correct dir fn) - - $ok = $this->_extractList($p_to, $p_what, $p_remdir, $p_mode); - $this->_CompTar(); - - return $ok; - } - - /** - * Create a new package with the given files - * - * This function will attempt to create a new archive with global headers - * then add the given files into. If the archive is a real file, the - * contents are written directly into the file. If it is a dynamic archive, - * contents are only stored in memory. This function should not be used to - * add files to an existing archive, you should use Add() instead. - * - * The FileList actually supports three different modes: - * - * - You can pass a string containing filenames separated by pipes '|'. - * In this case thes file are read from the filesystem and the root folder - * is the folder running script located. NOT RECOMMENDED - * - * - You can also give an indexed array containing the filenames. The - * behaviour for the content reading is the same as above. - * - * - You can pass an array of arrays. For each file use an array where the - * first element contains the filename and the second contains the file - * contents. You can even add empty folders to the package if the filename - * has a leading '/'. Once again, have a look at the exemples to understand - * better. - * - * Note you can also give arrays with both dynamic contents and static files. - * - * The optional parameter RemovePath can be used to delete a part of the tree - * of the filename you're adding, for instance if you're adding in the root - * of a package a file that is stored somewhere in the server tree. - * - * On the contrary the parameter AddPath can be used to add a prefix folder - * to the file you store. Note also that the RemovePath is applied before the - * AddPath is added, so it HAS a sense to use both parameters together. - */ - function Create($p_filelist, $p_add = '', $p_rem = '') { - if(!$fl = $this->_fetchFilelist($p_filelist)) return -5; - if(!$this->_OpenWrite()) return -6; - - $ok = $this->_addFileList($fl, $p_add, $p_rem); - - if($ok) { - $this->_writeFooter(); - } else { - $this->_CompTar(); - @unlink($this->_nomf); - } - - return $ok; - } - - /** - * Add files to an existing package. - * - * This function does exactly the same as Create() exept it - * will append the given files at the end of the archive. - * - * Note: This is only supported for dynamic in memory files and uncompressed - * tar files - * - * This function returns a status code, you can use TarErrorStr() on - * it to get the human-readable description of the error. - */ - function Add($p_filelist, $p_add = '', $p_rem = '') { - if($this->_nomf !== TarLib::ARCHIVE_DYNAMIC && - $this->_comptype !== TarLib::COMPRESS_NONE - ) { - return -12; - } - - if(($this->_nomf !== TarLib::ARCHIVE_DYNAMIC && !$this->_fp) || - ($this->_nomf === TarLib::ARCHIVE_DYNAMIC && !$this->_memdat) - ) { - return $this->Create($p_filelist, $p_add, $p_rem); - } - - if(!$fl = $this->_fetchFilelist($p_filelist)) return -5; - return $this->_append($fl, $p_add, $p_rem); - } - - /** - * Read the contents of a TAR archive - * - * This function attempts to get the list of the files stored in the - * archive, and return either an error code or an indexed array of - * associative array containing for each file the following information : - * - * checksum Tar Checksum of the file - * filename The full name of the stored file (up to 100 c.) - * mode UNIX permissions in DECIMAL, not octal - * uid The Owner ID - * gid The Group ID - * size Uncompressed filesize - * mtime Timestamp of last modification - * typeflag Empty for files, set for folders - * link For the links, did you guess it ? - * uname Owner name - * gname Group name - */ - function ListContents() { - if(!$this->_nomf) return -3; - if(!$this->_OpenRead()) return -4; - - $result = Array(); - - while($dat = $this->_read(512)) { - $dat = $this->_readHeader($dat); - if(!is_array($dat)) continue; - - $this->_seek(ceil($dat['size'] / 512) * 512, 1); - $result[] = $dat; - } - - return $result; - } - - /** - * Convert a status code into a human readable message - * - * Some MaxgTar functions like Create(), Add() ... return numerical - * status code. You can pass them to this function to grab their english - * equivalent. - */ - function TarErrorStr($i) { - $ecodes = Array( - 1 => true, - 0 => "Undocumented error", - -1 => "Can't use COMPRESS_GZIP compression : ZLIB extensions are not loaded !", - -2 => "Can't use COMPRESS_BZIP compression : BZ2 extensions are not loaded !", - -3 => "You must set a archive file to read the contents !", - -4 => "Can't open the archive file for read !", - -5 => "Invalide file list !", - -6 => "Can't open the archive in write mode !", - -7 => "There is no ARCHIVE_DYNAMIC to write !", - -8 => "Can't create the directory to extract files !", - -9 => "Please pass a archive name to send if you made created an ARCHIVE_DYNAMIC !", - -10 => "You didn't pass an archive filename and there is no stored ARCHIVE_DYNAMIC !", - -11 => "Given archive doesn't exist !", - -12 => "Appending not supported for compressed files" - ); - - return isset($ecodes[$i]) ? $ecodes[$i] : $ecodes[0]; - } - - /** - * Seek in the data stream - * - * @todo probably broken for bzip tars - * @param int $p_flen seek to this position - * @param bool $tell seek from current position? - */ - function _seek($p_flen, $tell = false) { - if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) - $this->_memdat = substr($this->_memdat, 0, ($tell ? strlen($this->_memdat) : 0) + $p_flen); - elseif($this->_comptype == TarLib::COMPRESS_GZIP) - @gzseek($this->_fp, ($tell ? @gztell($this->_fp) : 0) + $p_flen); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) - @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0) + $p_flen); - else - @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0) + $p_flen); - } - - /** - * Open the archive for reading - * - * @return bool true if succesfull - */ - function _OpenRead() { - if($this->_comptype == TarLib::COMPRESS_GZIP) - $this->_fp = @gzopen($this->_nomf, 'rb'); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) - $this->_fp = @bzopen($this->_nomf, 'rb'); - else - $this->_fp = @fopen($this->_nomf, 'rb'); - - return ($this->_fp ? true : false); - } - - /** - * Open the archive for writing - * - * @param string $add filemode - * @return bool true on success - */ - function _OpenWrite($add = 'w') { - if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) return true; - - if($this->_comptype == TarLib::COMPRESS_GZIP) - $this->_fp = @gzopen($this->_nomf, $add.'b'.$this->_compzlevel); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) - $this->_fp = @bzopen($this->_nomf, $add.'b'); - else - $this->_fp = @fopen($this->_nomf, $add.'b'); - - return ($this->_fp ? true : false); - } - - /** - * Closes the open file pointer - */ - function _CompTar() { - if($this->_nomf === TarLib::ARCHIVE_DYNAMIC || !$this->_fp) return; - - if($this->_comptype == TarLib::COMPRESS_GZIP) @gzclose($this->_fp); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) @bzclose($this->_fp); - else @fclose($this->_fp); - } - - /** - * Read from the open file pointer - * - * @param int $p_len bytes to read - * @return string - */ - function _read($p_len) { - if($this->_comptype == TarLib::COMPRESS_GZIP) - return @gzread($this->_fp, $p_len); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) - return @bzread($this->_fp, $p_len); - else - return @fread($this->_fp, $p_len); - } - - /** - * Write to the open filepointer or memory - * - * @param string $p_data - * @return int - */ - function _write($p_data) { - if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) { - $this->_memdat .= $p_data; - return strlen($p_data); - } elseif($this->_comptype == TarLib::COMPRESS_GZIP) { - return @gzwrite($this->_fp, $p_data); - } elseif($this->_comptype == TarLib::COMPRESS_BZIP) { - return @bzwrite($this->_fp, $p_data); - } else { - return @fwrite($this->_fp, $p_data); - } - } - - /** - * Compress given data according to the set compression method - * - * @param $p_dat - * @return string - */ - function _encode($p_dat) { - if($this->_comptype == TarLib::COMPRESS_GZIP) - return gzencode($p_dat, $this->_compzlevel); - elseif($this->_comptype == TarLib::COMPRESS_BZIP) - return bzcompress($p_dat, $this->_compzlevel); - else return $p_dat; - } - - /** - * Decode the given tar file header - * - * @param $p_dat - * @return array|bool - */ - function _readHeader($p_dat) { - if(!$p_dat || strlen($p_dat) != 512) return false; - - for($i = 0, $chks = 0; $i < 148; $i++) - $chks += ord($p_dat[$i]); - - for($i = 156, $chks += 256; $i < 512; $i++) - $chks += ord($p_dat[$i]); - - $headers = @unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $p_dat); - if(!$headers) return false; - - $return['checksum'] = OctDec(trim($headers['checksum'])); - if($return['checksum'] != $chks) return false; - - $return['filename'] = trim($headers['filename']); - $return['mode'] = OctDec(trim($headers['mode'])); - $return['uid'] = OctDec(trim($headers['uid'])); - $return['gid'] = OctDec(trim($headers['gid'])); - $return['size'] = OctDec(trim($headers['size'])); - $return['mtime'] = OctDec(trim($headers['mtime'])); - $return['typeflag'] = $headers['typeflag']; - $return['link'] = trim($headers['link']); - $return['uname'] = trim($headers['uname']); - $return['gname'] = trim($headers['gname']); - - return $return; - } - - /** - * Builds a normalized file list - * - * @todo remove string support, use saner format - * - * @param $p_filelist - * @return array|bool - */ - function _fetchFilelist($p_filelist) { - if(!$p_filelist || (is_array($p_filelist) && !@count($p_filelist))) return false; - - if(is_string($p_filelist)) { - $p_filelist = explode('|', $p_filelist); - if(!is_array($p_filelist)) $p_filelist = Array($p_filelist); - } - - return $p_filelist; - } - - /** - * Adds files given as file list - * - * @param array $p_fl - * @param string $p_addir - * @param string $p_remdir - * @return bool - */ - function _addFileList($p_fl, $p_addir, $p_remdir) { - foreach($p_fl as $file) { - if(($file == $this->_nomf && $this->_nomf !== TarLib::ARCHIVE_DYNAMIC) || !$file || (!is_array($file) && !file_exists($file))) - continue; - - if(!$this->_addFile($file, $p_addir, $p_remdir)) - continue; - - if(@is_dir($file)) { - $d = @opendir($file); - - if(!$d) continue; - readdir($d); - readdir($d); - - while($f = readdir($d)) { - if($file != ".") $tmplist[0] = "$file/$f"; - else $tmplist[0] = $d; - - $this->_addFileList($tmplist, $p_addir, $p_remdir); - } - - closedir($d); - unset($tmplist, $f); - } - } - return true; - } - - /** - * Adds a single file - * - * @param array|string $p_fn - * @param string $p_addir - * @param string $p_remdir - * @return bool - */ - function _addFile($p_fn, $p_addir = '', $p_remdir = '') { - $data = false; - if(is_array($p_fn)) list($p_fn, $data) = $p_fn; - $sname = $p_fn; - - if($p_remdir) { - if(substr($p_remdir, -1) != '/') $p_remdir .= "/"; - - if(substr($sname, 0, strlen($p_remdir)) == $p_remdir) - $sname = substr($sname, strlen($p_remdir)); - } - - if($p_addir) $sname = $p_addir.(substr($p_addir, -1) == '/' ? '' : "/").$sname; - - // FIXME ustar should support up 256 chars - if(strlen($sname) > 99) return false; - - if(@is_dir($p_fn)) { - if(!$this->_writeFileHeader($p_fn, $sname)) return false; - } else { - if(!$data) { - $fp = fopen($p_fn, 'rb'); - if(!$fp) return false; - } - - if(!$this->_writeFileHeader($p_fn, $sname, ($data ? strlen($data) : false))) return false; - - if(!$data) { - while(!feof($fp)) { - $packed = pack("a512", fread($fp, 512)); - $this->_write($packed); - } - fclose($fp); - } else { - $len = strlen($data); - for($s = 0; $s < $len; $s += 512) { - $this->_write(pack("a512", substr($data, $s, 512))); - } - } - } - - return true; - } - - /** - * Write the header for a file in the TAR archive - * - * @param string $p_file - * @param string $p_sname - * @param bool $p_data - * @return bool - */ - function _writeFileHeader($p_file, $p_sname, $p_data = false) { - if(!$p_data) { - if(!$p_sname) $p_sname = $p_file; - $p_sname = $this->_pathTrans($p_sname); - - $h_info = stat($p_file); - $h[0] = sprintf("%6s ", DecOct($h_info[4])); - $h[] = sprintf("%6s ", DecOct($h_info[5])); - $h[] = sprintf("%6s ", DecOct(fileperms($p_file))); - clearstatcache(); - $h[] = sprintf("%11s ", DecOct(filesize($p_file))); - $h[] = sprintf("%11s", DecOct(filemtime($p_file))); - - $dir = @is_dir($p_file) ? '5' : ''; - } else { - $dir = ''; - $p_data = sprintf("%11s ", DecOct($p_data)); - $time = sprintf("%11s ", DecOct(time())); - $h = Array(" 0 ", " 0 ", " 40777 ", $p_data, $time); - } - - $data_first = pack("a100a8a8a8a12A12", $p_sname, $h[2], $h[0], $h[1], $h[3], $h[4]); - $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', ""); - - for($i = 0, $chks = 0; $i < 148; $i++) - $chks += ord($data_first[$i]); - - for($i = 156, $chks += 256, $j = 0; $i < 512; $i++, $j++) - $chks += ord($data_last[$j]); - - $this->_write($data_first); - - $chks = pack("a8", sprintf("%6s ", DecOct($chks))); - $this->_write($chks.$data_last); - - return true; - } - - /** - * Append the given files to the already open archive - * - * @param array $p_filelist - * @param string $p_addir - * @param string $p_remdir - * @return bool|int - */ - function _append($p_filelist, $p_addir = "", $p_remdir = "") { - if(!$this->_fp) if(!$this->_OpenWrite('a')) return -6; - - if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) { - $this->_memdat = substr($this->_memdat, 0, -512 * 2); // remove footer - } else { - clearstatcache(); - $s = filesize($this->_nomf); - - $this->_seek($s - (512 * 2)); // remove footer - } - - $ok = $this->_addFileList($p_filelist, $p_addir, $p_remdir); - $this->_writeFooter(); - - return $ok; - } - - /** - * Cleans up a path and removes relative parts - * - * @param string $p_dir - * @return string - */ - function _pathTrans($p_dir) { - $r = ''; - if($p_dir) { - $subf = explode("/", $p_dir); - - for($i = count($subf) - 1; $i >= 0; $i--) { - if($subf[$i] == ".") { - # do nothing - } elseif($subf[$i] == "..") { - $i--; - } elseif(!$subf[$i] && $i != count($subf) - 1 && $i) { - # do nothing - } else { - $r = $subf[$i].($i != (count($subf) - 1) ? "/".$r : ""); - } - } - } - return $r; - } - - /** - * Add the closing footer to the archive - * - * Physically, an archive consists of a series of file entries terminated by an end-of-archive entry, which - * consists of two 512 blocks of zero bytes - * - * @link http://www.gnu.org/software/tar/manual/html_chapter/tar_8.html#SEC134 - */ - function _writeFooter() { - $this->_write(pack("a512", "")); - $this->_write(pack("a512", "")); - } - - /** - * @param $p_to - * @param $p_files - * @param $p_remdir - * @param int $p_mode - * @return array|bool|int|string - */ - function _extractList($p_to, $p_files, $p_remdir, $p_mode = 0755) { - if(!$p_to || ($p_to[0] != "/" && substr($p_to, 0, 3) != "../" && substr($p_to, 1, 3) != ":\\" && substr($p_to, 1, 2) != ":/")) /*" // <- PHP Coder bug */ - $p_to = "./$p_to"; - - if($p_remdir && substr($p_remdir, -1) != '/') $p_remdir .= '/'; - $p_remdirs = strlen($p_remdir); - while($dat = $this->_read(512)) { - $headers = $this->_readHeader($dat); - if(!$headers['filename']) continue; - - if($p_files == -1 || $p_files[0] == -1) { - $extract = true; - } else { - $extract = false; - - foreach($p_files as $f) { - if(substr($f, -1) == "/") { - if((strlen($headers['filename']) > strlen($f)) && (substr($headers['filename'], 0, strlen($f)) == $f)) { - $extract = true; - break; - } - } elseif($f == $headers['filename']) { - $extract = true; - break; - } - } - } - - if($extract) { - $det[] = $headers; - if($p_remdir && substr($headers['filename'], 0, $p_remdirs) == $p_remdir) - $headers['filename'] = substr($headers['filename'], $p_remdirs); - - if($headers['filename'].'/' == $p_remdir && $headers['typeflag'] == '5') continue; - - if($p_to != "./" && $p_to != "/") { - while($p_to{-1} == "/") $p_to = substr($p_to, 0, -1); - - if($headers['filename']{0} == "/") - $headers['filename'] = $p_to.$headers['filename']; - else - $headers['filename'] = $p_to."/".$headers['filename']; - } - - $ok = $this->_dirApp($headers['typeflag'] == "5" ? $headers['filename'] : dirname($headers['filename'])); - if($ok < 0) return $ok; - - if(!$headers['typeflag']) { - if(!$fp = @fopen($headers['filename'], "wb")) return -6; - $n = floor($headers['size'] / 512); - - for($i = 0; $i < $n; $i++) { - fwrite($fp, $this->_read(512), 512); - } - if(($headers['size'] % 512) != 0) fwrite($fp, $this->_read(512), $headers['size'] % 512); - - fclose($fp); - touch($headers['filename'], $headers['mtime']); - chmod($headers['filename'], $p_mode); - } else { - $this->_seek(ceil($headers['size'] / 512) * 512, 1); - } - } else $this->_seek(ceil($headers['size'] / 512) * 512, 1); - } - return $det; - } - - /** - * Create a directory hierarchy in filesystem - * - * @param string $d - * @return bool - */ - function _dirApp($d) { - // map to dokuwiki function (its more robust) - return io_mkdir_p($d); - } - -} - diff --git a/inc/load.php b/inc/load.php index 49c307054..2ea0d8efd 100644 --- a/inc/load.php +++ b/inc/load.php @@ -72,7 +72,6 @@ function load_autoload($name){ 'Doku_Plugin_Controller'=> DOKU_INC.'inc/plugincontroller.class.php', 'GeSHi' => DOKU_INC.'inc/geshi.php', 'Tar' => DOKU_INC.'inc/Tar.class.php', - 'TarLib' => DOKU_INC.'inc/TarLib.class.php', 'ZipLib' => DOKU_INC.'inc/ZipLib.class.php', 'DokuWikiFeedCreator' => DOKU_INC.'inc/feedcreator.class.php', 'Doku_Parser_Mode' => DOKU_INC.'inc/parser/parser.php', -- cgit v1.2.3 From 421a2704022cbc8fa07ab673c2d503199f460b8e Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 4 Nov 2012 10:36:58 +0100 Subject: Tar: Added extraction support for long file names Supports POSIX ustar prefixes and GNU longlink entries --- inc/Tar.class.php | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php index 7f5e5af4a..8da30e736 100644 --- a/inc/Tar.class.php +++ b/inc/Tar.class.php @@ -204,7 +204,7 @@ class Tar { // is this a file? if(!$header['typeflag']){ - $fp = @fopen($output, "wb"); + $fp = fopen($output, "wb"); if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output)); $size = floor($header['size'] / 512); @@ -508,7 +508,6 @@ class Tar { /** * Decode the given tar file header * - * @todo how to handle filenames >100 chars? * @param string $block a 512 byte block containign the header data * @return array|bool */ @@ -521,22 +520,36 @@ class Tar { for($i = 156, $chks += 256; $i < 512; $i++) $chks += ord($block[$i]); - $headers = @unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $block); - if(!$headers) return false; + $header = @unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $block); + if(!$header) return false; - $return['checksum'] = OctDec(trim($headers['checksum'])); + $return['checksum'] = OctDec(trim($header['checksum'])); if($return['checksum'] != $chks) return false; - $return['filename'] = trim($headers['filename']); - $return['perm'] = OctDec(trim($headers['perm'])); - $return['uid'] = OctDec(trim($headers['uid'])); - $return['gid'] = OctDec(trim($headers['gid'])); - $return['size'] = OctDec(trim($headers['size'])); - $return['mtime'] = OctDec(trim($headers['mtime'])); - $return['typeflag'] = $headers['typeflag']; - $return['link'] = trim($headers['link']); - $return['uname'] = trim($headers['uname']); - $return['gname'] = trim($headers['gname']); + $return['filename'] = trim($header['filename']); + $return['perm'] = OctDec(trim($header['perm'])); + $return['uid'] = OctDec(trim($header['uid'])); + $return['gid'] = OctDec(trim($header['gid'])); + $return['size'] = OctDec(trim($header['size'])); + $return['mtime'] = OctDec(trim($header['mtime'])); + $return['typeflag'] = $header['typeflag']; + $return['link'] = trim($header['link']); + $return['uname'] = trim($header['uname']); + $return['gname'] = trim($header['gname']); + + // Handle ustar Posix compliant path prefixes + if(trim($header['prefix'])) $return['filename'] = trim($header['prefix']).'/'.$return['filename']; + + // Handle Long-Link entries from GNU Tar + if($return['typeflag'] == 'L'){ + // following data block(s) is the filename + $filename = trim($this->readbytes(ceil($header['size'] / 512) * 512)); + // next block is the real header + $block = $this->readbytes(512); + $return = $this->parseHeader($block); + // overwrite the filename + $return['filename'] = $filename; + } return $return; } -- cgit v1.2.3 From 90a1db709d3590e849a5a4966fbdf8fb58ae75cd Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 4 Nov 2012 11:31:20 +0100 Subject: Tar: support for creating archives with long filenames The library now creates either a POSIX ustar prefix or a GNU longlink entry for files which have a name longer than 100 bytes --- inc/Tar.class.php | 121 +++++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 57 deletions(-) (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php index 8da30e736..59e14c705 100644 --- a/inc/Tar.class.php +++ b/inc/Tar.class.php @@ -4,6 +4,8 @@ * To keep things simple, the modification of existing archives is not supported. It handles * uncompressed, gzip and bzip2 compressed tar files. * + * Long pathnames (>100 chars) are supported in POSIX ustar and GNU longlink formats. + * * To list the contents of an existing TAR archive, open() it and use contents() on it: * * $tar = new Tar(); @@ -145,10 +147,10 @@ class Tar { * @throws TarIOException * @return array */ - function extract($outdir, $strip='', $exclude='', $include='') { + function extract($outdir, $strip = '', $exclude = '', $include = '') { if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive')); - $outdir = rtrim($outdir,'/'); + $outdir = rtrim($outdir, '/'); io_mkdir_p($outdir); $striplen = strlen($strip); @@ -164,32 +166,32 @@ class Tar { $filename = $this->cleanPath($header['filename']); if(is_int($strip)) { // if $strip is an integer we strip this many path components - $parts = explode('/',$filename); - if(!$header['typeflag']){ + $parts = explode('/', $filename); + if(!$header['typeflag']) { $base = array_pop($parts); // keep filename itself - }else{ + } else { $base = ''; } - $filename = join('/',array_slice($parts,$strip)); + $filename = join('/', array_slice($parts, $strip)); if($base) $filename .= "/$base"; - }else{ + } else { // ifstrip is a string, we strip a prefix here - if(substr($filename,0,$striplen) == $strip) $filename = substr($filename,$striplen); + if(substr($filename, 0, $striplen) == $strip) $filename = substr($filename, $striplen); } // check if this should be extracted $extract = true; - if(!$filename){ + if(!$filename) { $extract = false; - }else{ - if($include){ - if(preg_match($include, $filename)){ + } else { + if($include) { + if(preg_match($include, $filename)) { $extract = true; - }else{ + } else { $extract = false; } } - if($exclude && preg_match($exclude, $filename)){ + if($exclude && preg_match($exclude, $filename)) { $extract = false; } } @@ -203,7 +205,7 @@ class Tar { io_mkdir_p($directory); // is this a file? - if(!$header['typeflag']){ + if(!$header['typeflag']) { $fp = fopen($output, "wb"); if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output)); @@ -216,10 +218,10 @@ class Tar { fclose($fp); touch($output, $header['mtime']); chmod($output, $header['perm']); - }else{ + } else { $this->skipbytes(ceil($header['size'] / 512) * 512); // the size is usually 0 for directories } - }else{ + } else { $this->skipbytes(ceil($header['size'] / 512) * 512); } } @@ -261,7 +263,7 @@ class Tar { if(!$this->fh) throw(new TarIOException('Could not open file for writing: '.$this->file)); } $this->writeaccess = false; - $this->closed = false; + $this->closed = false; } /** @@ -270,7 +272,6 @@ class Tar { * @todo handle directory adding * @param string $file the original file * @param string $name the name to use for the file in the archive - * @throws TarBadFilename * @throws TarIOException */ public function addFile($file, $name = '') { @@ -279,9 +280,6 @@ class Tar { if(!$name) $name = $file; $name = $this->cleanPath($name); - // FIXME ustar should support up 256 chars - if(strlen($name) > 99) throw(new TarBadFilename('Filenames may not exceed 99 bytes: '.$name)); - $fp = fopen($file, 'rb'); if(!$fp) throw(new TarIOException('Could not open file for reading: '.$file)); @@ -294,8 +292,7 @@ class Tar { $stat[5], fileperms($file), filesize($file), - filemtime($file), - false + filemtime($file) ); while(!feof($fp)) { @@ -306,26 +303,21 @@ class Tar { } /** - * Add a file to the current TAR archive using in memory data + * Add a file to the current TAR archive using the given $data as content * - * @param $name - * @param $data - * @param int $uid - * @param int $gid - * @param int $perm - * @param int $mtime + * @param string $name + * @param string $data + * @param int $uid + * @param int $gid + * @param int $perm + * @param int $mtime * @throws TarIOException - * @throws TarBadFilename */ public function addData($name, $data, $uid = 0, $gid = 0, $perm = 0666, $mtime = 0) { if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added')); $name = $this->cleanPath($name); - - // FIXME ustar should support up 256 chars - if(strlen($name) > 99) throw(new TarBadFilename('Filenames may not exceed 99 bytes: '.$name)); - - $len = strlen($data); + $len = strlen($data); $this->writeFileHeader( $name, @@ -333,8 +325,7 @@ class Tar { $gid, $perm, $len, - ($mtime) ? $mtime : time(), - false + ($mtime) ? $mtime : time() ); for($s = 0; $s < $len; $s += 512) { @@ -357,23 +348,23 @@ class Tar { if($this->closed) return; // we did this already // write footer - if($this->writeaccess){ + if($this->writeaccess) { $this->writebytes(pack("a512", "")); $this->writebytes(pack("a512", "")); } // close file handles - if($this->file){ - if($this->comptype === Tar::COMPRESS_GZIP){ + if($this->file) { + if($this->comptype === Tar::COMPRESS_GZIP) { gzclose($this->fh); - }elseif($this->comptype === Tar::COMPRESS_BZIP){ + } elseif($this->comptype === Tar::COMPRESS_BZIP) { bzclose($this->fh); - }else{ + } else { fclose($this->fh); } $this->file = ''; - $this->fh = 0; + $this->fh = 0; } $this->closed = true; @@ -460,12 +451,12 @@ class Tar { * @param int $bytes seek to this position */ function skipbytes($bytes) { - if($this->comptype === Tar::COMPRESS_GZIP){ + if($this->comptype === Tar::COMPRESS_GZIP) { @gzseek($this->fh, $bytes, SEEK_CUR); - }elseif($this->comptype === Tar::COMPRESS_BZIP){ + } elseif($this->comptype === Tar::COMPRESS_BZIP) { // there is no seek in bzip2, we simply read on @bzread($this->fh, $bytes); - }else{ + } else { @fseek($this->fh, $bytes, SEEK_CUR); } } @@ -479,19 +470,38 @@ class Tar { * @param int $perm * @param int $size * @param int $mtime - * @param bool $isdir + * @param string $typeflag Set to '5' for directories */ - protected function writeFileHeader($name, $uid, $gid, $perm, $size, $mtime, $isdir = false) { + protected function writeFileHeader($name, $uid, $gid, $perm, $size, $mtime, $typeflag = '') { + // handle filename length restrictions + $prefix = ''; + $namelen = strlen($name); + if($namelen > 100) { + $file = basename($name); + $dir = dirname($name); + if(strlen($file) > 100 || strlen($dir) > 155) { + // we're still too large, let's use GNU longlink + $this->writeFileHeader('././@LongLink', 0, 0, 0, $namelen, 0, 'L'); + for($s = 0; $s < $namelen; $s += 512) { + $this->writebytes(pack("a512", substr($name, $s, 512))); + } + $name = substr($name, 0, 100); // cut off name + } else { + // we're fine when splitting, use POSIX ustar + $prefix = $dir; + $name = $file; + } + } + // values are needed in octal $uid = sprintf("%6s ", DecOct($uid)); $gid = sprintf("%6s ", DecOct($gid)); $perm = sprintf("%6s ", DecOct($perm)); $size = sprintf("%11s ", DecOct($size)); $mtime = sprintf("%11s", DecOct($mtime)); - $dir = ($isdir) ? '5' : ''; $data_first = pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime); - $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', ""); + $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, ""); for($i = 0, $chks = 0; $i < 148; $i++) $chks += ord($data_first[$i]); @@ -541,11 +551,11 @@ class Tar { if(trim($header['prefix'])) $return['filename'] = trim($header['prefix']).'/'.$return['filename']; // Handle Long-Link entries from GNU Tar - if($return['typeflag'] == 'L'){ + if($return['typeflag'] == 'L') { // following data block(s) is the filename $filename = trim($this->readbytes(ceil($header['size'] / 512) * 512)); // next block is the real header - $block = $this->readbytes(512); + $block = $this->readbytes(512); $return = $this->parseHeader($block); // overwrite the filename $return['filename'] = $filename; @@ -617,9 +627,6 @@ class Tar { } } -class TarBadFilename extends Exception { -} - class TarIOException extends Exception { } -- cgit v1.2.3 From 35349ab097171f0f9cb6114cffd780abb69c56a6 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Tue, 6 Nov 2012 20:57:25 +0100 Subject: added simple compatibility wrapper for old TarLib --- inc/TarLib.class.php | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ inc/load.php | 1 + 2 files changed, 88 insertions(+) create mode 100644 inc/TarLib.class.php (limited to 'inc') diff --git a/inc/TarLib.class.php b/inc/TarLib.class.php new file mode 100644 index 000000000..ae08039ec --- /dev/null +++ b/inc/TarLib.class.php @@ -0,0 +1,87 @@ +error('__construct', '$file'); + + $this->file = $file; + switch($comptype) { + case TarLib::COMPRESS_AUTO: + case TarLib::COMPRESS_DETECT: + $comptype = Tar::COMPRESS_AUTO; + break; + case TarLib::COMPRESS_GZIP: + $comptype = Tar::COMPRESS_GZIP; + break; + case TarLib::COMPRESS_BZIP: + $comptype = Tar::COMPRESS_BZIP; + break; + default: + $comptype = Tar::COMPRESS_NONE; + } + + $this->complevel = $complevel; + + try { + $this->tar = new Tar(); + $this->tar->open($file, $comptype); + } catch(Exception $e) { + $this->_result = false; + } + } + + function Extract($p_what = TarLib::FULL_ARCHIVE, $p_to = '.', $p_remdir = '', $p_mode = 0755) { + if($p_what != TarLib::FULL_ARCHIVE) { + $this->error('Extract', 'Ep_what'); + return 0; + } + + try { + $this->tar->extract($p_to, $p_remdir); + } catch(Exception $e) { + return 0; + } + return 1; + } + + function error($func, $param = '') { + $error = 'TarLib is deprecated and should no longer be used.'; + + if($param) { + $error .= "In this compatibility wrapper, the function '$func' does not accept your value for". + "the parameter '$param' anymore."; + } else { + $error .= "The function '$func' no longer exists in this compatibility wrapper."; + } + + msg($error, -1); + } + + function __call($name, $arguments) { + $this->error($name); + } +} \ No newline at end of file diff --git a/inc/load.php b/inc/load.php index 2ea0d8efd..49c307054 100644 --- a/inc/load.php +++ b/inc/load.php @@ -72,6 +72,7 @@ function load_autoload($name){ 'Doku_Plugin_Controller'=> DOKU_INC.'inc/plugincontroller.class.php', 'GeSHi' => DOKU_INC.'inc/geshi.php', 'Tar' => DOKU_INC.'inc/Tar.class.php', + 'TarLib' => DOKU_INC.'inc/TarLib.class.php', 'ZipLib' => DOKU_INC.'inc/ZipLib.class.php', 'DokuWikiFeedCreator' => DOKU_INC.'inc/feedcreator.class.php', 'Doku_Parser_Mode' => DOKU_INC.'inc/parser/parser.php', -- cgit v1.2.3 From 3161005d07beb46bb8a866ec56a768938571ec9d Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Mon, 12 Nov 2012 21:22:26 +0100 Subject: check for unicode preg capabilities in UTF-8 lib FS#2636 We now have two defines for checking for UTF-8 and Unicode property support in PREG and use them to work around FS#2636 on older systems. --- inc/fulltext.php | 15 +++++++++++---- inc/infoutils.php | 4 ++-- inc/utf8.php | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) (limited to 'inc') diff --git a/inc/fulltext.php b/inc/fulltext.php index eab8850dc..7ee386063 100644 --- a/inc/fulltext.php +++ b/inc/fulltext.php @@ -394,10 +394,17 @@ function ft_snippet_re_preprocess($term) { return $term; } - // unicode word boundaries - // see http://stackoverflow.com/a/2449017/172068 - $BL = '(? Date: Mon, 12 Nov 2012 21:29:23 +0100 Subject: Czech language update --- inc/lang/cs/lang.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'inc') diff --git a/inc/lang/cs/lang.php b/inc/lang/cs/lang.php index a53da327a..cfb8e8eb3 100644 --- a/inc/lang/cs/lang.php +++ b/inc/lang/cs/lang.php @@ -12,6 +12,7 @@ * @author Vojta Beran * @author zbynek.krivka@seznam.cz * @author Bohumir Zamecnik + * @author Jakub A. Těšínský (j@kub.cz) */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -197,6 +198,7 @@ $lang['user_tools'] = 'Uživatelské nástroje'; $lang['site_tools'] = 'Nástroje pro tento web'; $lang['page_tools'] = 'Nástroje pro stránku'; $lang['skip_to_content'] = 'jít k obsahu'; +$lang['sidebar'] = 'Postranní lišta'; $lang['mail_newpage'] = 'nová stránka:'; $lang['mail_changed'] = 'změna stránky:'; $lang['mail_subscribe_list'] = 'stránky změněné ve jmenném prostoru:'; -- cgit v1.2.3 From de78e4ef14515e4248d5f637558ec451e018799c Mon Sep 17 00:00:00 2001 From: Anael Mobilia Date: Mon, 12 Nov 2012 21:43:53 +0100 Subject: French language update --- inc/lang/fr/admin.txt | 3 +- inc/lang/fr/adminplugins.txt | 2 +- inc/lang/fr/backlinks.txt | 2 +- inc/lang/fr/conflict.txt | 4 +- inc/lang/fr/diff.txt | 2 +- inc/lang/fr/draft.txt | 4 +- inc/lang/fr/edit.txt | 2 +- inc/lang/fr/index.txt | 4 +- inc/lang/fr/install.html | 10 +-- inc/lang/fr/lang.php | 172 +++++++++++++++++++++--------------------- inc/lang/fr/locked.txt | 2 +- inc/lang/fr/mailtext.txt | 2 +- inc/lang/fr/mailwrap.html | 2 +- inc/lang/fr/newpage.txt | 2 +- inc/lang/fr/norev.txt | 2 +- inc/lang/fr/password.txt | 2 +- inc/lang/fr/preview.txt | 2 +- inc/lang/fr/pwconfirm.txt | 8 +- inc/lang/fr/read.txt | 2 +- inc/lang/fr/register.txt | 2 +- inc/lang/fr/registermail.txt | 6 +- inc/lang/fr/resendpwd.txt | 2 +- inc/lang/fr/stopwords.txt | 4 +- inc/lang/fr/subscr_digest.txt | 4 +- inc/lang/fr/subscr_form.txt | 2 +- inc/lang/fr/subscr_list.txt | 2 +- inc/lang/fr/subscr_single.txt | 4 +- inc/lang/fr/uploadmail.txt | 4 +- 28 files changed, 130 insertions(+), 129 deletions(-) (limited to 'inc') diff --git a/inc/lang/fr/admin.txt b/inc/lang/fr/admin.txt index 4477a512b..eeeb2317a 100644 --- a/inc/lang/fr/admin.txt +++ b/inc/lang/fr/admin.txt @@ -1,4 +1,3 @@ ====== Administration ====== -Ci-dessous, vous trouverez une liste des tâches administratives disponibles dans DokuWiki. - +Ci-dessous, vous trouverez une liste des tâches d'administration disponibles dans DokuWiki. diff --git a/inc/lang/fr/adminplugins.txt b/inc/lang/fr/adminplugins.txt index 42a3538ab..0b2bf18db 100644 --- a/inc/lang/fr/adminplugins.txt +++ b/inc/lang/fr/adminplugins.txt @@ -1 +1 @@ -===== Modules supplémentaires ===== \ No newline at end of file +===== Extensions ===== \ No newline at end of file diff --git a/inc/lang/fr/backlinks.txt b/inc/lang/fr/backlinks.txt index 6902b43e3..8e6d27d95 100644 --- a/inc/lang/fr/backlinks.txt +++ b/inc/lang/fr/backlinks.txt @@ -1,4 +1,4 @@ ====== Pages pointant sur la page en cours ====== -Ceci est la liste des pages qui pointent sur la page en cours. +Ceci est la liste des pages qui semblent pointer sur la page actuelle. diff --git a/inc/lang/fr/conflict.txt b/inc/lang/fr/conflict.txt index 8f527ee21..e34ec9743 100644 --- a/inc/lang/fr/conflict.txt +++ b/inc/lang/fr/conflict.txt @@ -1,6 +1,6 @@ -====== Une version plus récente existe déjà ====== +====== Une version plus récente existe ====== -Une version plus récente du document que vous avez modifié existe déjà. Cela se produit lorsqu'un autre utilisateur enregistre le document pendant que vous le modifiez. +Une version plus récente du document que vous avez modifié existe. Cela se produit lorsqu'un autre utilisateur enregistre une nouvelle version du document alors que vous le modifiez. Examinez attentivement les différences ci-dessous et décidez quelle version conserver. Si vous choisissez « Enregistrer », votre version sera enregistrée. Cliquez sur « Annuler » pour conserver la version actuelle. diff --git a/inc/lang/fr/diff.txt b/inc/lang/fr/diff.txt index 773695d6d..8569f3497 100644 --- a/inc/lang/fr/diff.txt +++ b/inc/lang/fr/diff.txt @@ -1,4 +1,4 @@ ====== Différences ====== -Cette page vous donne les différences entre la révision choisie et la version actuelle de la page. +Cette page vous affiche les différences entre la révision choisie et la version actuelle de la page. diff --git a/inc/lang/fr/draft.txt b/inc/lang/fr/draft.txt index a48554298..ab383ee2c 100644 --- a/inc/lang/fr/draft.txt +++ b/inc/lang/fr/draft.txt @@ -1,6 +1,6 @@ ====== Un fichier brouillon a été trouvé ====== -La dernière modification de cette page ne s'est pas terminée proprement. Dokuwiki a enregistré automatiquement un brouillon de votre travail que vous pouvez utiliser pour votre modification. Ci-dessous figurent les données enregistrées lors de votre dernière session. +La dernière modification de cette page ne s'est pas terminée correctement. DokuWiki a enregistré automatiquement un brouillon de votre travail que vous pouvez utiliser pour votre modification. Ci-dessous figurent les données enregistrées lors de votre dernière session. -À vous de décider si vous souhaitez //récupérer// votre session de modification passée, //supprimer// le brouillon enregistré automatiquement ou //annuler// le processus d'édition. +À vous de décider si vous souhaitez //récupérer// votre session de modification précédente, //supprimer// le brouillon enregistré automatiquement ou //annuler// le processus d'édition. diff --git a/inc/lang/fr/edit.txt b/inc/lang/fr/edit.txt index e30f1b78b..df8c9fc9e 100644 --- a/inc/lang/fr/edit.txt +++ b/inc/lang/fr/edit.txt @@ -1,2 +1,2 @@ -Modifiez cette page et cliquez sur « Enregistrer ». Voyez le [[:wiki:syntax|guide de la mise en page]] pour une aide à propos du formatage. Veuillez ne modifier cette page que si vous pouvez l'**améliorer**. Si vous souhaitez faire des tests, faites vos premiers pas dans le [[:playground:playground|bac à sable]]. +Modifiez cette page et cliquez sur « Enregistrer ». Voyez le [[:wiki:syntax|guide de mise en page]] pour une aide à propos du formatage. Veuillez ne modifier cette page que si vous pouvez l'**améliorer**. Si vous souhaitez faire des tests, faites vos premiers pas dans le [[:playground:playground|bac à sable]]. diff --git a/inc/lang/fr/index.txt b/inc/lang/fr/index.txt index c66c656ab..15e16734c 100644 --- a/inc/lang/fr/index.txt +++ b/inc/lang/fr/index.txt @@ -1,4 +1,4 @@ -====== Index ====== +====== Plan du site ====== -Voici un index de toutes les pages disponibles, triées par [[doku>fr:namespaces|catégorie]]. +Voici un plan du site de toutes les pages disponibles, triées par [[doku>fr:namespaces|catégories]]. diff --git a/inc/lang/fr/install.html b/inc/lang/fr/install.html index 91c9f2edf..6dcba25dc 100644 --- a/inc/lang/fr/install.html +++ b/inc/lang/fr/install.html @@ -1,13 +1,13 @@ -

Cette page vous assiste dans la première installation et la +

Cette page vous assiste dans l'installation et la configuration de DokuWiki. -Pour plus d'information sur cet installeur, reportez-vous à sa +Pour plus d'informations sur cet installateur, reportez-vous à sa page de documentation.

DokuWiki utilise des fichiers textes ordinaires pour stocker les pages du wiki et les autres informations associées à ces pages -(tel que images, index de recherche, anciennes révisions, etc.). Pour fonctionner correctement, DokuWiki doit avoir accès en écriture aux différents répertoires qui contiennent ces fichiers. L'installeur n'est pas capable de modifier les permissions sur les répertoires. Ceci doit être effectué directement sur la ligne de commande de votre shell, ou, si vous êtes hébergé, via FTP ou votre panneau de contrôle (tel que cPanel).

+(par exemple, les images, les index de recherche, les anciennes révisions, ...). Pour fonctionner correctement, DokuWiki doit avoir accès en écriture aux différents répertoires qui contiennent ces fichiers. Cet installateur n'est pas capable de modifier les autorisations sur les répertoires. Cette opération doit-être effectué directement depuis votre ligne de commande shell, ou, si vous êtes hébergé, via FTP ou votre panneau de contrôle (par exemple cPanel, Plesk, ...).

-

Cet installeur va paramétrer votre configuration de DokuWiki pour des ACL, qui permettront l'accès à un identifiant administrateur et l'accès au menu d'administration de DokuWiki pour l'ajout de modules externes (greffons), la gestion d'utilisateurs, la gestion de l'accès aux pages du wiki et les modifications des paramètres de configuration. Il n'est pas nécessaire au fonctionnement de DokuWiki, néanmoins il facilite l'administration de DokuWiki.

+

Cet installateur va paramétrer votre configuration de DokuWiki pour des contrôle d'accès (ACL), qui permettront l'accès à un identifiant administrateur et l'accès au menu d'administration de DokuWiki pour l'ajout d'extensions, la gestion d'utilisateurs, la gestion de l'accès aux pages du wiki et les modifications des paramètres de configuration. Les contrôle d'accès ne sont pas nécessaires au fonctionnement de DokuWiki, néanmoins elles facilitent l'administration de DokuWiki.

-

Les utilisateurs expérimentés ou ceux nécessitant des paramétrages particuliers devraient se reporter aux liens suivants pour les détails concernant les instructions d'installation et les paramètres de configuration.

+

Les utilisateurs expérimentés ou les utilisateurs possédants des besoins de configurations spécifiques devraient se reporter aux liens suivants pour les détails concernant les instructions d'installation et les paramètres de configuration.

diff --git a/inc/lang/fr/lang.php b/inc/lang/fr/lang.php index c4080bc50..44cf04e04 100644 --- a/inc/lang/fr/lang.php +++ b/inc/lang/fr/lang.php @@ -25,6 +25,7 @@ * @author skimpax@gmail.com * @author Yannick Aure * @author Olivier DUVAL + * @author Anael Mobilia */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -73,17 +74,17 @@ $lang['user'] = 'Utilisateur'; $lang['pass'] = 'Mot de passe'; $lang['newpass'] = 'Nouveau mot de passe'; $lang['oldpass'] = 'Mot de passe actuel'; -$lang['passchk'] = 'Répéter nouveau mot de passe'; +$lang['passchk'] = 'Répétez le mot de passe'; $lang['remember'] = 'Mémoriser'; $lang['fullname'] = 'Nom'; $lang['email'] = 'Adresse de courriel'; $lang['profile'] = 'Profil utilisateur'; $lang['badlogin'] = 'L\'utilisateur ou le mot de passe est incorrect.'; $lang['minoredit'] = 'Modification mineure'; -$lang['draftdate'] = 'Brouillon auto-enregistré le'; +$lang['draftdate'] = 'Brouillon enregistré de manière automatique le'; $lang['nosecedit'] = 'La page a changé entre temps, les informations de la section sont obsolètes ; la page complète a été chargée à la place.'; $lang['regmissing'] = 'Désolé, vous devez remplir tous les champs.'; -$lang['reguexists'] = 'Désolé, ce nom d\'utilisateur est déjà pris.'; +$lang['reguexists'] = 'Désolé, ce nom d\'utilisateur est déjà utilisé.'; $lang['regsuccess'] = 'L\'utilisateur a été créé. Le mot de passe a été expédié par courriel.'; $lang['regsuccess2'] = 'L\'utilisateur a été créé.'; $lang['regmailfail'] = 'Il semble y avoir un problème à l\'envoi du courriel. Contactez l\'administrateur.'; @@ -92,94 +93,94 @@ $lang['regbadpass'] = 'Les deux mots de passe fournis sont différent $lang['regpwmail'] = 'Votre mot de passe DokuWiki'; $lang['reghere'] = 'Vous n\'avez pas encore de compte ? Enregistrez-vous ici '; $lang['profna'] = 'Ce wiki ne permet pas de modifier les profils'; -$lang['profnochange'] = 'Pas de changement, rien à faire.'; +$lang['profnochange'] = 'Pas de modification, rien à faire.'; $lang['profnoempty'] = 'Un nom ou une adresse de courriel vide n\'est pas permis.'; $lang['profchanged'] = 'Mise à jour du profil réussie.'; -$lang['pwdforget'] = 'Mot de passe oublié ? Faites-vous envoyer votre mot de passe '; +$lang['pwdforget'] = 'Mot de passe oublié ? Obtenez-en un nouveau'; $lang['resendna'] = 'Ce wiki ne permet pas le renvoi de mot de passe.'; $lang['resendpwd'] = 'Définir un nouveau mot de passe pour'; $lang['resendpwdmissing'] = 'Désolé, vous devez remplir tous les champs.'; -$lang['resendpwdnouser'] = 'Désolé, cet utilisateur est introuvable dans notre base.'; -$lang['resendpwdbadauth'] = 'Désolé, ce code d\'authentification est invalide. Assurez-vous d\'avoir utilisé le lien de confirmation.'; -$lang['resendpwdconfirm'] = 'Un lien de confirmation vous a été envoyé par courriel.'; +$lang['resendpwdnouser'] = 'Désolé, cet utilisateur n\'existe pas dans notre base de données.'; +$lang['resendpwdbadauth'] = 'Désolé, ce code d\'authentification est invalide. Assurez-vous d\'avoir utilisé le lien de confirmation intégral.'; +$lang['resendpwdconfirm'] = 'Un lien de confirmation vous a été expédié par courriel.'; $lang['resendpwdsuccess'] = 'Votre nouveau mot de passe vous a été expédié par courriel.'; -$lang['license'] = 'Sauf mention contraire, le contenu de ce wiki est placé sous la licence suivante :'; +$lang['license'] = 'Sauf mention contraire, le contenu de ce wiki est placé sous les termes de la licence suivante :'; $lang['licenseok'] = 'Note : En modifiant cette page, vous acceptez que le contenu soit placé sous les termes de la licence suivante :'; $lang['searchmedia'] = 'Chercher le nom de fichier :'; $lang['searchmedia_in'] = 'Chercher dans %s'; $lang['txt_upload'] = 'Sélectionnez un fichier à envoyer '; -$lang['txt_filename'] = 'Donnez un « wikiname » (optionnel) '; -$lang['txt_overwrt'] = 'Écraser le fichier cible'; -$lang['maxuploadsize'] = 'Téléverser max. %s par fichier'; +$lang['txt_filename'] = 'Envoyer en tant que (optionnel) '; +$lang['txt_overwrt'] = 'Écraser le fichier cible (s\'il existe)'; +$lang['maxuploadsize'] = 'Taille d\'envoi maximale : %s par fichier'; $lang['lockedby'] = 'Actuellement bloqué par'; $lang['lockexpire'] = 'Le blocage expire à'; -$lang['js']['willexpire'] = 'Votre verrouillage pour la modification de cette page expire dans une minute.\nPour éviter les conflits, utilisez le bouton « Aperçu » pour réinitialiser le minuteur.'; +$lang['js']['willexpire'] = 'Votre blocage pour la modification de cette page expire dans une minute.\nPour éviter les conflits, utilisez le bouton « Aperçu » pour réinitialiser le minuteur.'; $lang['js']['notsavedyet'] = 'Les modifications non enregistrées seront perdues. Voulez-vous vraiment continuer ?'; $lang['js']['searchmedia'] = 'Chercher des fichiers'; -$lang['js']['keepopen'] = 'Gardez cette fenêtre toujours ouverte'; -$lang['js']['hidedetails'] = 'Masquer détails'; +$lang['js']['keepopen'] = 'Toujours conserver cette fenêtre ouverte'; +$lang['js']['hidedetails'] = 'Masquer les détails'; $lang['js']['mediatitle'] = 'Paramètres de lien'; $lang['js']['mediadisplay'] = 'Type de lien'; $lang['js']['mediaalign'] = 'Alignement'; -$lang['js']['mediasize'] = 'Taille d\'image'; +$lang['js']['mediasize'] = 'Taille de l\'image'; $lang['js']['mediatarget'] = 'Cible du lien'; $lang['js']['mediaclose'] = 'Fermer'; $lang['js']['mediainsert'] = 'Insérer'; $lang['js']['mediadisplayimg'] = 'Afficher l\'image.'; $lang['js']['mediadisplaylnk'] = 'N\'afficher que le lien.'; $lang['js']['mediasmall'] = 'Petite taille'; -$lang['js']['mediamedium'] = 'taille moyenne'; +$lang['js']['mediamedium'] = 'Taille moyenne'; $lang['js']['medialarge'] = 'Grande taille'; -$lang['js']['mediaoriginal'] = 'taille d\'origine'; +$lang['js']['mediaoriginal'] = 'Taille originelle'; $lang['js']['medialnk'] = 'Lien vers la page de détail'; $lang['js']['mediadirect'] = 'Lien direct vers l\'original'; $lang['js']['medianolnk'] = 'Aucun lien'; $lang['js']['medianolink'] = 'Ne pas lier l\'image'; -$lang['js']['medialeft'] = 'Aligner l\'image sur la gauche.'; -$lang['js']['mediaright'] = 'Aligner l\'image sur la droite.'; -$lang['js']['mediacenter'] = 'Centrer l\'image'; +$lang['js']['medialeft'] = 'Aligner l\'image à gauche.'; +$lang['js']['mediaright'] = 'Aligner l\'image à droite.'; +$lang['js']['mediacenter'] = 'Centrer l\'image.'; $lang['js']['medianoalign'] = 'Ne pas aligner.'; $lang['js']['nosmblinks'] = 'Les liens vers les partages Windows ne fonctionnent qu\'avec Microsoft Internet Explorer.\nVous pouvez toujours copier puis coller le lien.'; $lang['js']['linkwiz'] = 'Assistant Lien'; $lang['js']['linkto'] = 'Lien vers :'; -$lang['js']['del_confirm'] = 'Effacer cette entrée ?'; -$lang['js']['restore_confirm'] = 'Voulez vous vraiment restaurer cette version ?'; -$lang['js']['media_diff'] = 'Voir les différences:'; +$lang['js']['del_confirm'] = 'Voulez-vous vraiment effacer ce(s) élément(s) ?'; +$lang['js']['restore_confirm'] = 'Voulez-vous vraiment restaurer cette version ?'; +$lang['js']['media_diff'] = 'Voir les différences :'; $lang['js']['media_diff_both'] = 'Côte à côte'; $lang['js']['media_diff_opacity'] = 'Calque'; $lang['js']['media_diff_portions'] = 'Curseur'; $lang['js']['media_select'] = 'Sélection de fichiers…'; -$lang['js']['media_upload_btn'] = 'Télécharger'; +$lang['js']['media_upload_btn'] = 'Envoyer'; $lang['js']['media_done_btn'] = 'Terminé'; -$lang['js']['media_drop'] = 'Déposez des fichiers ici pour les télécharger'; +$lang['js']['media_drop'] = 'Déposez des fichiers ici pour les envoyer'; $lang['js']['media_cancel'] = 'supprimer'; $lang['js']['media_overwrt'] = 'Écraser les fichiers existants'; $lang['rssfailed'] = 'Une erreur s\'est produite en récupérant ce flux : '; $lang['nothingfound'] = 'Pas de réponse.'; -$lang['mediaselect'] = 'Sélection de fichier'; +$lang['mediaselect'] = 'Sélection de fichiers'; $lang['fileupload'] = 'Envoi de fichier'; -$lang['uploadsucc'] = 'Téléversement réussi'; -$lang['uploadfail'] = 'Le téléversement n\'a pas réussi. Les permissions sont-elles correctes ?'; -$lang['uploadwrong'] = 'Téléversement refusé. Cette extension de fichier est interdite !'; -$lang['uploadexist'] = 'Le fichier existe. Téléversement avorté.'; -$lang['uploadbadcontent'] = 'Le contenu envoyé ne correspond pas à l\'extension du fichier %s.'; -$lang['uploadspam'] = 'Le téléversement a été bloqué par la liste noire antispam.'; -$lang['uploadxss'] = 'Le téléversement a été bloqué car son contenu est peut-être malveillant.'; -$lang['uploadsize'] = 'Le fichier téléversé était trop gros. (max. %s)'; +$lang['uploadsucc'] = 'Envoi réussi'; +$lang['uploadfail'] = 'L\'envoi a échoué. Les autorisations sont-elles correctes ?'; +$lang['uploadwrong'] = 'Envoi refusé. Cette extension de fichier est interdite !'; +$lang['uploadexist'] = 'Le fichier existe déjà. L\'envoi a été annulé.'; +$lang['uploadbadcontent'] = 'Le contenu envoyé ne correspond pas à l\'extension du fichier (%s).'; +$lang['uploadspam'] = 'L\'envoi a été bloqué par la liste noire de l\'anti-spam.'; +$lang['uploadxss'] = 'L\'envoi a été bloqué car son contenu est peut-être malveillant.'; +$lang['uploadsize'] = 'Le fichier envoyé était trop gros. (max. : %s)'; $lang['deletesucc'] = 'Le fichier « %s » a été effacé.'; -$lang['deletefail'] = 'Le fichier « %s » n\'a pu être effacé, vérifier les permissions.'; -$lang['mediainuse'] = 'Le fichier « %s » n\'a pas été effacé, il est en cours d\'utilisation.'; +$lang['deletefail'] = 'Le fichier « %s » n\'a pas pu être effacé. Vérifiez les autorisations.'; +$lang['mediainuse'] = 'Le fichier « %s » n\'a pas été effacé : il est en toujours utilisé.'; $lang['namespaces'] = 'Catégories'; $lang['mediafiles'] = 'Fichiers disponibles dans'; $lang['accessdenied'] = 'Vous n\'êtes pas autorisé à voir cette page.'; $lang['mediausage'] = 'Utilisez la syntaxe suivante pour faire référence à ce fichier :'; $lang['mediaview'] = 'Afficher le fichier original'; $lang['mediaroot'] = 'racine'; -$lang['mediaupload'] = 'Téléverser un fichier dans la catégorie actuelle. Pour créer des sous-catégories, préfixez le nom du fichier par le nom de la sous-catégorie séparée par un double-point.'; -$lang['mediaextchange'] = 'Extension du fichier changée de .%s en .%s !'; +$lang['mediaupload'] = 'Envoyez un fichier dans la catégorie actuelle. Pour créer des sous-catégories, préfixez en le nom du fichier séparées par un double-point, après avoir choisis le(s) fichier(s). Le(s) fichier(s) peuvent également être envoyé(s) par glisser-déposer (drag & drop)'; +$lang['mediaextchange'] = 'Extension du fichier modifiée de .%s en .%s !'; $lang['reference'] = 'Références pour'; -$lang['ref_inuse'] = 'Le fichier ne peut être effacé car il est utilisé par les pages suivantes :'; -$lang['ref_hidden'] = 'Des références existent dans des pages que vous n\'avez pas la permission de lire'; +$lang['ref_inuse'] = 'Le fichier ne peut être effacé car il est toujours utilisé par les pages suivantes :'; +$lang['ref_hidden'] = 'Des références sont présentes dans des pages que vous ne pouvez pas voir (autorisations insuffisantes)'; $lang['hits'] = 'Occurrences trouvées'; $lang['quickhits'] = 'Pages trouvées '; $lang['toc'] = 'Table des matières'; @@ -187,7 +188,7 @@ $lang['current'] = 'Version actuelle'; $lang['yours'] = 'Votre version'; $lang['diff'] = 'Différences avec la version actuelle'; $lang['diff2'] = 'Différences entre les versions sélectionnées'; -$lang['difflink'] = 'Lien vers cette vue'; +$lang['difflink'] = 'Lien vers cette vue comparative'; $lang['diff_type'] = 'Voir les différences :'; $lang['diff_inline'] = 'Sur une seule ligne'; $lang['diff_side'] = 'Côte à côte'; @@ -196,18 +197,19 @@ $lang['breadcrumb'] = 'Piste'; $lang['youarehere'] = 'Vous êtes ici'; $lang['lastmod'] = 'Dernière modification'; $lang['by'] = 'par'; -$lang['deleted'] = 'effacée'; +$lang['deleted'] = 'supprimée'; $lang['created'] = 'créée'; -$lang['restored'] = 'ancienne révision restaurée'; +$lang['restored'] = 'ancienne révision (%s) restaurée'; $lang['external_edit'] = 'modification externe'; $lang['summary'] = 'Résumé'; -$lang['noflash'] = 'Le greffon Adobe Flash est nécessaire pour afficher ce contenu.'; +$lang['noflash'] = 'L\'extension Adobe Flash est nécessaire pour afficher ce contenu.'; $lang['download'] = 'Télécharger un extrait'; $lang['tools'] = 'Outils'; -$lang['user_tools'] = 'Outils d\'utilisateurs'; -$lang['site_tools'] = 'Outils du Site'; -$lang['page_tools'] = 'Outils de la Page'; +$lang['user_tools'] = 'Outils pour utilisateurs'; +$lang['site_tools'] = 'Outils du site'; +$lang['page_tools'] = 'Outils de la page'; $lang['skip_to_content'] = 'Aller au contenu'; +$lang['sidebar'] = 'Panneau latéral'; $lang['mail_newpage'] = 'page ajoutée :'; $lang['mail_changed'] = 'page modifiée :'; $lang['mail_subscribe_list'] = 'pages modifiées dans la catégorie :'; @@ -215,13 +217,13 @@ $lang['mail_new_user'] = 'nouvel utilisateur :'; $lang['mail_upload'] = 'fichier envoyé :'; $lang['changes_type'] = 'Voir les changements'; $lang['pages_changes'] = 'Pages'; -$lang['media_changes'] = 'Fichier multimédias'; +$lang['media_changes'] = 'Fichiers multimédias'; $lang['both_changes'] = 'Pages et fichiers multimédias'; -$lang['qb_bold'] = 'Emphase forte (gras)'; -$lang['qb_italic'] = 'Emphase (italique)'; -$lang['qb_underl'] = 'Souligné'; +$lang['qb_bold'] = 'Gras'; +$lang['qb_italic'] = 'Italique'; +$lang['qb_underl'] = 'Soulignage'; $lang['qb_code'] = 'Code « machine à écrire »'; -$lang['qb_strike'] = 'Texte barré'; +$lang['qb_strike'] = 'Barré'; $lang['qb_h1'] = 'Titre de niveau 1'; $lang['qb_h2'] = 'Titre de niveau 2'; $lang['qb_h3'] = 'Titre de niveau 3'; @@ -237,14 +239,14 @@ $lang['qb_extlink'] = 'Lien externe'; $lang['qb_hr'] = 'Ligne horizontale'; $lang['qb_ol'] = 'Liste numérotée'; $lang['qb_ul'] = 'Liste à puce'; -$lang['qb_media'] = 'Ajouter des images ou d\'autres fichiers'; +$lang['qb_media'] = 'Ajouter des images ou autres fichiers'; $lang['qb_sig'] = 'Insérer une signature'; $lang['qb_smileys'] = 'Émoticones'; $lang['qb_chars'] = 'Caractères spéciaux'; $lang['upperns'] = 'Aller à la catégorie parente'; $lang['admin_register'] = 'Ajouter un nouvel utilisateur'; $lang['metaedit'] = 'Modifier les métadonnées'; -$lang['metasaveerr'] = 'Erreur lors de l\'écriture des métadonnées'; +$lang['metasaveerr'] = 'Erreur lors de l\'enregistrement des métadonnées'; $lang['metasaveok'] = 'Métadonnées enregistrées'; $lang['img_backto'] = 'Retour à'; $lang['img_title'] = 'Titre'; @@ -252,7 +254,7 @@ $lang['img_caption'] = 'Légende'; $lang['img_date'] = 'Date'; $lang['img_fname'] = 'Nom de fichier'; $lang['img_fsize'] = 'Taille'; -$lang['img_artist'] = 'Auteur'; +$lang['img_artist'] = 'Photographe'; $lang['img_copyr'] = 'Copyright'; $lang['img_format'] = 'Format'; $lang['img_camera'] = 'Appareil photo'; @@ -261,47 +263,47 @@ $lang['img_width'] = 'Largeur'; $lang['img_height'] = 'Hauteur'; $lang['img_manager'] = 'Voir dans le gestionnaire de médias'; $lang['subscr_subscribe_success'] = '%s a été ajouté à la liste de souscription de %s'; -$lang['subscr_subscribe_error'] = 'Erreur en ajoutant %s à la liste de souscription de %s'; +$lang['subscr_subscribe_error'] = 'Erreur à l\'ajout de %s à la liste de souscription de %s'; $lang['subscr_subscribe_noaddress'] = 'Il n\'y a pas d\'adresse associée à votre identifiant, vous ne pouvez pas être ajouté à la liste de souscription'; -$lang['subscr_unsubscribe_success'] = '%s a été retiré de la liste de souscription de %s'; -$lang['subscr_unsubscribe_error'] = 'Erreur en retirant %s de la liste de souscription de %s'; +$lang['subscr_unsubscribe_success'] = '%s a été supprimé de la liste de souscription de %s'; +$lang['subscr_unsubscribe_error'] = 'Erreur au retrait de %s de la liste de souscription de %s'; $lang['subscr_already_subscribed'] = '%s est déjà souscrit à %s'; $lang['subscr_not_subscribed'] = '%s n\'est pas souscrit à %s'; -$lang['subscr_m_not_subscribed'] = 'Vous n\'avez pas souscrit pour l\'instant à la page actuelle ou la catégorie'; +$lang['subscr_m_not_subscribed'] = 'Vous n\'avez pas souscrit pour l\'instant à la page actuelle ou à la catégorie'; $lang['subscr_m_new_header'] = 'Ajouter une souscription'; $lang['subscr_m_current_header'] = 'Souscriptions actives'; $lang['subscr_m_unsubscribe'] = 'Annuler la souscription'; $lang['subscr_m_subscribe'] = 'Souscrire'; $lang['subscr_m_receive'] = 'Recevoir'; -$lang['subscr_style_every'] = 'Envoyer un courriel à chaque modification'; +$lang['subscr_style_every'] = 'Recevoir un courriel à chaque modification'; $lang['subscr_style_digest'] = 'Courriel, tous les %.2f jours, résumant les modifications de chaque page'; $lang['subscr_style_list'] = 'Liste des pages modifiées depuis le dernier courriel (tous les %.2f jours)'; -$lang['authmodfailed'] = 'Mauvais paramétrage de l\'authentification. Merci d\'informer l\'administrateur du Wiki.'; -$lang['authtempfail'] = 'L\'authentification est temporairement indisponible. Si cela perdure, merci d\'informer l\'administrateur du Wiki.'; +$lang['authmodfailed'] = 'Mauvais paramétrage de l\'authentification. Merci d\'en informer l\'administrateur du wiki.'; +$lang['authtempfail'] = 'L\'authentification est temporairement indisponible. Si cela perdure, merci d\'en informer l\'administrateur du wiki.'; $lang['authpwdexpire'] = 'Votre mot de passe expirera dans %d jours, vous devriez le changer bientôt.'; $lang['i_chooselang'] = 'Choisissez votre langue'; -$lang['i_installer'] = 'Installeur DokuWiki'; +$lang['i_installer'] = 'Installateur DokuWiki'; $lang['i_wikiname'] = 'Nom du wiki'; -$lang['i_enableacl'] = 'Activer les ACL (recommandé)'; +$lang['i_enableacl'] = 'Activer le contrôle d\'accès (recommandé)'; $lang['i_superuser'] = 'Super-utilisateur'; -$lang['i_problems'] = 'L\'installeur a détecté les problèmes indiqués ci-dessous. Vous ne pouvez poursuivre tant qu\'ils n\'auront pas été corrigés.'; -$lang['i_modified'] = 'Pour des raisons de sécurité ce script ne fonctionne qu\'avec une installation neuve et non modifiée de DokuWiki. Vous devriez ré-extraire les fichiers depuis le paquet téléchargé ou consulter les instructions d\'installation de DokuWiki'; -$lang['i_funcna'] = 'La fonction PHP %s n\'est pas disponible. Peut-être que votre hébergeur l\'a désactivée ?'; +$lang['i_problems'] = 'L\'installateur a détecté les problèmes indiqués ci-dessous. Vous ne pouvez pas poursuivre l\'installation tant qu\'ils n\'auront pas été corrigés.'; +$lang['i_modified'] = 'Pour des raisons de sécurité, ce script ne fonctionne qu\'avec une installation neuve et non modifiée de DokuWiki. Vous devriez ré-extraire les fichiers depuis le paquet téléchargé ou consulter les instructions d\'installation de DokuWiki'; +$lang['i_funcna'] = 'La fonction PHP %s n\'est pas disponible. Peut-être que votre hébergeur web l\'a désactivée ?'; $lang['i_phpver'] = 'Votre version de PHP (%s) est antérieure à la version requise (%s). Vous devez mettre à jour votre installation de PHP.'; -$lang['i_permfail'] = '%s n\'est pas accessible en écriture pour DokuWiki. Vous devez corriger les permissions de ce répertoire !'; +$lang['i_permfail'] = '%s n\'est pas accessible en écriture pour DokuWiki. Vous devez corriger les autorisations de ce répertoire !'; $lang['i_confexists'] = '%s existe déjà'; -$lang['i_writeerr'] = 'Impossible de créer %s. Vous devez vérifier les permissions des répertoires/fichiers et créer le fichier manuellement.'; +$lang['i_writeerr'] = 'Impossible de créer %s. Vous devez vérifier les autorisations des répertoires/fichiers et créer le fichier manuellement.'; $lang['i_badhash'] = 'dokuwiki.php non reconnu ou modifié (hash=%s)'; $lang['i_badval'] = '%s - valeur interdite ou vide'; $lang['i_success'] = 'L\'installation s\'est terminée avec succès. Vous pouvez maintenant supprimer le fichier « install.php ». Continuer avec votre nouveau DokuWiki.'; $lang['i_failure'] = 'Des erreurs sont survenues lors de l\'écriture des fichiers de configuration. Il vous faudra les corriger manuellement avant de pouvoir utiliser votre nouveau DokuWiki.'; -$lang['i_policy'] = 'Politique d\'ACL initiale'; +$lang['i_policy'] = 'Politique de contrôle d\'accès initiale'; $lang['i_pol0'] = 'Wiki ouvert (lecture, écriture, envoi de fichiers pour tout le monde)'; $lang['i_pol1'] = 'Wiki public (lecture pour tout le monde, écriture et envoi de fichiers pour les utilisateurs enregistrés)'; $lang['i_pol2'] = 'Wiki fermé (lecture, écriture, envoi de fichiers pour les utilisateurs enregistrés uniquement)'; $lang['i_retry'] = 'Réessayer'; -$lang['i_license'] = 'Veuillez choisir la licence sous laquelle placer votre contenu :'; -$lang['recent_global'] = 'Vous êtes actuellement en train de regarder les modifications au sein de la catégorie %s. Vous pouvez aussi voir les récentes modifications sur tout le wiki.'; +$lang['i_license'] = 'Veuillez choisir la licence sous laquelle vous souhaitez placer votre contenu :'; +$lang['recent_global'] = 'Vous êtes actuellement en train de regarder les modifications au sein de la catégorie %s. Vous pouvez également afficher les derniers changements sur l\'ensemble du wiki.'; $lang['years'] = 'il y a %d ans'; $lang['months'] = 'il y a %d mois'; $lang['weeks'] = 'il y a %d semaines'; @@ -309,27 +311,27 @@ $lang['days'] = 'il y a %d jours'; $lang['hours'] = 'il y a %d heures'; $lang['minutes'] = 'il y a %d minutes'; $lang['seconds'] = 'il y a %d secondes'; -$lang['wordblock'] = 'Vos modifications n\'ont pas été sauvegardées parce qu\'elles contiennent des textes non autorisé (spam).'; -$lang['media_uploadtab'] = 'Télécharger'; +$lang['wordblock'] = 'Vos modifications n\'ont pas été enregistrées car elles contiennent du texte non autorisé (spam).'; +$lang['media_uploadtab'] = 'Envoyer'; $lang['media_searchtab'] = 'Rechercher'; $lang['media_file'] = 'Fichier'; $lang['media_viewtab'] = 'Voir'; $lang['media_edittab'] = 'Éditer'; $lang['media_historytab'] = 'Historique'; -$lang['media_list_thumbs'] = 'Aperçus'; +$lang['media_list_thumbs'] = 'Miniatures'; $lang['media_list_rows'] = 'Lignes'; -$lang['media_sort_name'] = 'Tri par nom'; -$lang['media_sort_date'] = 'Tri par date'; -$lang['media_namespaces'] = 'Choisissez un espace de nom'; -$lang['media_files'] = 'Fichiers de %s'; -$lang['media_upload'] = 'Télécharger dans %s.'; -$lang['media_search'] = 'Chercher dans %s.'; +$lang['media_sort_name'] = 'Nom'; +$lang['media_sort_date'] = 'Date'; +$lang['media_namespaces'] = 'Choisissez une catégorie'; +$lang['media_files'] = 'Fichiers dans %s'; +$lang['media_upload'] = 'Envoyer vers %s.'; +$lang['media_search'] = 'Rechercher dans %s.'; $lang['media_view'] = '%s'; $lang['media_viewold'] = '%s dans %s'; $lang['media_edit'] = 'Éditer %s'; $lang['media_history'] = 'Historique de %s'; $lang['media_meta_edited'] = 'métadonnées éditées'; -$lang['media_perm_read'] = 'Désolé, vous n\'avez pas les droits pour lire les fichiers.'; -$lang['media_perm_upload'] = 'Désolé, vous n\'avez pas les droits pour télécharger des fichiers.'; -$lang['media_update'] = 'Télécharger une nouvelle version'; +$lang['media_perm_read'] = 'Désolé, vous n\'avez pas l\'autorisation de voir les fichiers.'; +$lang['media_perm_upload'] = 'Désolé, vous n\'avez pas l\'autorisation d\'envoyer des fichiers.'; +$lang['media_update'] = 'Envoyer une nouvelle version'; $lang['media_restore'] = 'Restaurer cette version'; diff --git a/inc/lang/fr/locked.txt b/inc/lang/fr/locked.txt index 82cdd7373..fe88b57c8 100644 --- a/inc/lang/fr/locked.txt +++ b/inc/lang/fr/locked.txt @@ -1,3 +1,3 @@ ====== Page bloquée ====== -Cette page est actuellement bloquée pour modification par un autre utilisateur. Vous devez attendre que l'autre utilisateur ait terminé ou que le blocage de la page expire. +Cette page est actuellement bloquée pour modification par un autre utilisateur. Vous devez attendre que cet utilisateur ait terminé ou que le blocage de la page expire. diff --git a/inc/lang/fr/mailtext.txt b/inc/lang/fr/mailtext.txt index 3c2d53292..3f191756a 100644 --- a/inc/lang/fr/mailtext.txt +++ b/inc/lang/fr/mailtext.txt @@ -14,5 +14,5 @@ Utilisateur : @USER@ -- -Ce courriel a été généré par DokuWiki +Ce courriel a été généré par DokuWiki depuis @DOKUWIKIURL@ diff --git a/inc/lang/fr/mailwrap.html b/inc/lang/fr/mailwrap.html index 2b674196b..aa5011021 100644 --- a/inc/lang/fr/mailwrap.html +++ b/inc/lang/fr/mailwrap.html @@ -8,6 +8,6 @@ @HTMLBODY@

-Ce courriel a été automatiquement généré par DokuWiki à l'adresse @DOKUWIKIURL@. +Ce courriel a été automatiquement généré par DokuWiki depuis @DOKUWIKIURL@. \ No newline at end of file diff --git a/inc/lang/fr/newpage.txt b/inc/lang/fr/newpage.txt index 0ed2b25af..b23bf4fe4 100644 --- a/inc/lang/fr/newpage.txt +++ b/inc/lang/fr/newpage.txt @@ -1,4 +1,4 @@ ====== Cette page n'existe pas encore ====== -Vous avez suivi un lien vers une page qui n'existe pas encore. Si vos droits sont suffisants, vous pouvez utiliser le bouton ou le lien « Créer cette page ». +Vous avez suivi un lien vers une page qui n'existe pas encore. Si vos autorisations sont suffisants, vous pouvez la créer en cliquant sur « Créer cette page ». diff --git a/inc/lang/fr/norev.txt b/inc/lang/fr/norev.txt index 3f96b6aff..0d40dbe05 100644 --- a/inc/lang/fr/norev.txt +++ b/inc/lang/fr/norev.txt @@ -1,4 +1,4 @@ ====== Révision non trouvée ====== -La révision demandée n'existe pas. Utilisez le bouton ou le lien « Anciennes révisions » pour une liste des révisions de ce document. +La révision demandée n'existe pas. Cliquez sur « Anciennes révisions » pour obtenir une liste des révisions de ce document. diff --git a/inc/lang/fr/password.txt b/inc/lang/fr/password.txt index f4500fc85..47cb391d2 100644 --- a/inc/lang/fr/password.txt +++ b/inc/lang/fr/password.txt @@ -6,5 +6,5 @@ Utilisateur : @LOGIN@ Mot de passe : @PASSWORD@ -- -Ce courriel a été envoyé par DokuWiki de +Ce courriel a été envoyé par DokuWiki depuis @DOKUWIKIURL@ diff --git a/inc/lang/fr/preview.txt b/inc/lang/fr/preview.txt index 26fbcd9c2..00f09e218 100644 --- a/inc/lang/fr/preview.txt +++ b/inc/lang/fr/preview.txt @@ -1,4 +1,4 @@ ====== Aperçu ====== -Ceci est un aperçu de votre document. Attention ! Il n'est **pas encore enregistré** ! +Ceci est un aperçu de votre document. Attention : il n'est **pas encore enregistré** ! diff --git a/inc/lang/fr/pwconfirm.txt b/inc/lang/fr/pwconfirm.txt index af84833df..5cbfb2241 100644 --- a/inc/lang/fr/pwconfirm.txt +++ b/inc/lang/fr/pwconfirm.txt @@ -1,15 +1,15 @@ Bonjour @FULLNAME@ ! Quelqu'un a demandé un nouveau mot de passe pour votre identifiant -@TITLE@ sur @DOKUWIKIURL@ +@TITLE@ depuis @DOKUWIKIURL@ Si vous n'êtes pas à l'origine de cette requête d'un nouveau mot de -passe, ignorez ce message. +passe, ignorez simplement ce message. -Pour confirmer que cette requête émane bien de vous, merci de suivre le lien ci-dessous. +Pour confirmer que cette requête émane bien de vous, merci de cliquer sur le lien ci-dessous. @CONFIRM@ -- -Ce courriel a été généré par DokuWiki +Ce courriel a été généré par DokuWiki depuis @DOKUWIKIURL@ diff --git a/inc/lang/fr/read.txt b/inc/lang/fr/read.txt index faa756e8b..6afb864a8 100644 --- a/inc/lang/fr/read.txt +++ b/inc/lang/fr/read.txt @@ -1,2 +1,2 @@ -Cette page est en lecture seule. Vous pouvez afficher le texte source, mais pas le modifier. Contactez votre administrateur si vous pensez qu'il s'agit d'une erreur. +Cette page est en lecture seule. Vous pouvez afficher le texte source, mais ne pourrez pas le modifier. Contactez votre administrateur si vous pensez qu'il s'agit d'une erreur. diff --git a/inc/lang/fr/register.txt b/inc/lang/fr/register.txt index e2d02f55c..f98383454 100644 --- a/inc/lang/fr/register.txt +++ b/inc/lang/fr/register.txt @@ -1,3 +1,3 @@ ====== S'enregistrer comme nouvel utilisateur ====== -Remplissez toutes les informations ci-dessous pour vous créer un compte sur ce Wiki. Assurez-vous de fournir une **adresse de courriel valide** car votre mot de passe sera envoyé à cette adresse. Le nom d'utilisateur doit être un [[doku>pagename|nom de page]] valide. +Remplissez toutes les informations ci-dessous pour vous créer un compte sur ce wiki. Assurez-vous de fournir une **adresse de courriel valide** - s'il ne vous est pas demandé de saisir un mot de passe ici, il vous sera expédié par courriel à cette adresse. Le nom d'utilisateur doit être un [[doku>pagename|nom de page]] valide. diff --git a/inc/lang/fr/registermail.txt b/inc/lang/fr/registermail.txt index 1beae8522..43d72dba9 100644 --- a/inc/lang/fr/registermail.txt +++ b/inc/lang/fr/registermail.txt @@ -2,13 +2,13 @@ Un nouvel utilisateur s'est enregistré. Voici les détails : Utilisateur : @NEWUSER@ Nom : @NEWNAME@ -Adresse de courriel : @NEWEMAIL@ +Courriel : @NEWEMAIL@ Date : @DATE@ -Navigateur : @BROWSER@ +Navigateur internet : @BROWSER@ Adresse IP : @IPADDRESS@ Nom d'hôte : @HOSTNAME@ -- -Ce courriel a été généré par DokuWiki +Ce courriel a été généré par DokuWiki depuis @DOKUWIKIURL@ diff --git a/inc/lang/fr/resendpwd.txt b/inc/lang/fr/resendpwd.txt index 44fbeef03..91dd92482 100644 --- a/inc/lang/fr/resendpwd.txt +++ b/inc/lang/fr/resendpwd.txt @@ -1,4 +1,4 @@ ====== Envoyer un nouveau mot de passe ====== -Veuillez compléter les champs ci-dessous pour obtenir un nouveau mot de passe pour votre compte dans ce wiki. Un lien de confirmation vous sera envoyé à l'adresse de courriel utilisée lors de votre enregistrement. +Veuillez compléter les champs ci-dessous pour obtenir un nouveau mot de passe pour votre compte dans ce wiki. Un lien de confirmation vous sera expédié à l'adresse de courriel utilisée lors de votre enregistrement. diff --git a/inc/lang/fr/stopwords.txt b/inc/lang/fr/stopwords.txt index 981bae26b..5f187f7e1 100644 --- a/inc/lang/fr/stopwords.txt +++ b/inc/lang/fr/stopwords.txt @@ -1,5 +1,5 @@ -# Cette liste regroupe des mots ignorés par l'indexeur -# Chaque ligne comporte un mot +# Cette liste regroupe les mots ignorés par l'indexeur +# Un seul mot par ligne # Les fins de ligne de ce fichier doivent être de type UNIX # Les mots de moins de 3 lettres sont ignorés par défaut. # Cette liste est basée sur http://www.ranks.nl/stopwords/ diff --git a/inc/lang/fr/subscr_digest.txt b/inc/lang/fr/subscr_digest.txt index 1803407fa..7ec75ca76 100644 --- a/inc/lang/fr/subscr_digest.txt +++ b/inc/lang/fr/subscr_digest.txt @@ -1,6 +1,6 @@ Bonjour, -La page « @PAGE@ » dans le wiki « @TITLE@ » a été modifiée. Voici ces modifications : +La page « @PAGE@ » dans le wiki « @TITLE@ » a été modifiée. Voici les modifications : -------------------------------------------------------- @DIFF@ @@ -15,5 +15,5 @@ Pour annuler les notifications de page, connectez-vous au wiki à l'adresse et désabonnez-vous de la page ou de la catégorie. -- -Ce courriel a été généré par Dokuwiki : +Ce courriel a été généré par DokuWiki depuis @DOKUWIKIURL@ \ No newline at end of file diff --git a/inc/lang/fr/subscr_form.txt b/inc/lang/fr/subscr_form.txt index 528f77475..49c0cf443 100644 --- a/inc/lang/fr/subscr_form.txt +++ b/inc/lang/fr/subscr_form.txt @@ -1,3 +1,3 @@ ====== Gestion de l'abonnement ====== -Cette page vous permet de gérer vos abonnements à la page ou à la catégorie courantes \ No newline at end of file +Cette page vous permet de gérer vos abonnements à la page et à la catégorie courantes \ No newline at end of file diff --git a/inc/lang/fr/subscr_list.txt b/inc/lang/fr/subscr_list.txt index 3387b11ee..d8c6b68e4 100644 --- a/inc/lang/fr/subscr_list.txt +++ b/inc/lang/fr/subscr_list.txt @@ -1,6 +1,6 @@ Bonjour, -Des pages dans la catégorie « @PAGE@ » du wiki « @TITLE@ » ont été modifiées. Voici ces modifications : +Des pages de la catégorie « @PAGE@ » du wiki « @TITLE@ » ont été modifiées. Voici les modifications : -------------------------------------------------------- @DIFF@ diff --git a/inc/lang/fr/subscr_single.txt b/inc/lang/fr/subscr_single.txt index 1b9d5e1b5..236d45e8f 100644 --- a/inc/lang/fr/subscr_single.txt +++ b/inc/lang/fr/subscr_single.txt @@ -1,6 +1,6 @@ Bonjour, -La page « @PAGE@ » dans le wiki « @TITLE@ » a été modifiée. Voici ces modifications : +La page « @PAGE@ » dans le wiki « @TITLE@ » a été modifiée. Voici les modifications : -------------------------------------------------------- @DIFF@ @@ -18,5 +18,5 @@ Pour annuler les notifications de page, connectez-vous au wiki à l'adresse et désabonnez-vous de la page ou de la catégorie. -- -Ce courriel a été généré par Dokuwiki : +Ce courriel a été généré par Dokuwiki depuis @DOKUWIKIURL@ \ No newline at end of file diff --git a/inc/lang/fr/uploadmail.txt b/inc/lang/fr/uploadmail.txt index 05b3205d7..80be0de8c 100644 --- a/inc/lang/fr/uploadmail.txt +++ b/inc/lang/fr/uploadmail.txt @@ -1,4 +1,4 @@ -Un fichier a été téléversé dans votre wiki. En voici les détails : +Un fichier a été envoyé dans votre wiki. Voici les détails : Fichier : @MEDIA@ Date : @DATE@ @@ -10,5 +10,5 @@ Type MIME : @MIME@ Utilisateur : @USER@ -- -Ce message a été généré par DokuWiki +Ce message a été généré par DokuWiki depuis @DOKUWIKIURL@ -- cgit v1.2.3 From c786a1b6200b051466db67a1791f46bbd375bb3b Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Sun, 18 Nov 2012 12:02:20 +0000 Subject: fixed wrong sidebar showing in namespaces when sidebar is disabled --- inc/pageutils.php | 1 + inc/template.php | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/pageutils.php b/inc/pageutils.php index 3bb10883f..ef82eebcd 100644 --- a/inc/pageutils.php +++ b/inc/pageutils.php @@ -645,6 +645,7 @@ function utf8_decodeFN($file){ * @return string|false the full page id of the found page, false if any */ function page_findnearest($page){ + if (!$page) return false; global $ID; $ns = $ID; diff --git a/inc/template.php b/inc/template.php index 0d96be214..4af35cc2b 100644 --- a/inc/template.php +++ b/inc/template.php @@ -1471,11 +1471,10 @@ function tpl_license($img = 'badge', $imgonly = false, $return = false, $wrap = * template */ function tpl_include_page($pageid, $print = true, $propagate = false) { - global $ID; - global $TOC; - + if (!$pageid) return false; if ($propagate) $pageid = page_findnearest($pageid); + global $TOC; $oldtoc = $TOC; $html = p_wiki_xhtml($pageid, '', false); $TOC = $oldtoc; -- cgit v1.2.3 From 2c2ee174e46096ce890518061ca8e2e0866cf67c Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Sun, 18 Nov 2012 18:52:13 +0000 Subject: changed semicolon to colon in link to welcome page to make it less confusing --- inc/lang/ar/lang.php | 4 ++-- inc/lang/az/lang.php | 4 ++-- inc/lang/bg/lang.php | 6 +++--- inc/lang/ca-valencia/lang.php | 4 ++-- inc/lang/ca/lang.php | 4 ++-- inc/lang/cs/lang.php | 4 ++-- inc/lang/da/lang.php | 4 ++-- inc/lang/de-informal/lang.php | 4 ++-- inc/lang/de/lang.php | 4 ++-- inc/lang/el/lang.php | 4 ++-- inc/lang/en/lang.php | 4 ++-- inc/lang/eo/lang.php | 4 ++-- inc/lang/es/lang.php | 4 ++-- inc/lang/et/lang.php | 4 ++-- inc/lang/eu/lang.php | 4 ++-- inc/lang/fa/lang.php | 4 ++-- inc/lang/fi/lang.php | 4 ++-- inc/lang/fr/lang.php | 4 ++-- inc/lang/gl/lang.php | 4 ++-- inc/lang/he/lang.php | 4 ++-- inc/lang/hr/lang.php | 4 ++-- inc/lang/hu/lang.php | 4 ++-- inc/lang/ia/lang.php | 4 ++-- inc/lang/id/lang.php | 4 ++-- inc/lang/it/lang.php | 4 ++-- inc/lang/ja/lang.php | 4 ++-- inc/lang/ko/lang.php | 4 ++-- inc/lang/la/lang.php | 4 ++-- inc/lang/lv/lang.php | 4 ++-- inc/lang/mk/lang.php | 4 ++-- inc/lang/mr/lang.php | 4 ++-- inc/lang/ne/lang.php | 4 ++-- inc/lang/nl/lang.php | 4 ++-- inc/lang/no/lang.php | 4 ++-- inc/lang/pl/lang.php | 4 ++-- inc/lang/pt-br/lang.php | 4 ++-- inc/lang/pt/lang.php | 4 ++-- inc/lang/ro/lang.php | 4 ++-- inc/lang/ru/lang.php | 4 ++-- inc/lang/sk/lang.php | 4 ++-- inc/lang/sl/lang.php | 4 ++-- inc/lang/sq/lang.php | 4 ++-- inc/lang/sr/lang.php | 4 ++-- inc/lang/sv/lang.php | 4 ++-- inc/lang/tr/lang.php | 4 ++-- inc/lang/uk/lang.php | 4 ++-- inc/lang/zh-tw/lang.php | 4 ++-- inc/lang/zh/lang.php | 4 ++-- 48 files changed, 97 insertions(+), 97 deletions(-) (limited to 'inc') diff --git a/inc/lang/ar/lang.php b/inc/lang/ar/lang.php index 0f5566471..85c88d10f 100644 --- a/inc/lang/ar/lang.php +++ b/inc/lang/ar/lang.php @@ -284,8 +284,8 @@ $lang['i_badhash'] = 'الملف dokuwiki.php غير مصنف أو (hash=%s)'; $lang['i_badval'] = 'القيمة %s غير شرعية أو فارغة'; $lang['i_success'] = 'الإعدادات تمت بنجاح، يرجى حذف الملف install.php الآن. -ثم تابع إلى دوكو ويكي الجديدة'; -$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; +ثم تابع إلى دوكو ويكي الجديدة'; +$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; $lang['i_policy'] = 'تصريح ACL مبدئي'; $lang['i_pol0'] = 'ويكي مفتوحة؛ أي القراءة والكتابة والتحميل مسموحة للجميع'; $lang['i_pol1'] = 'ويكي عامة؛ أي القراءة للجميع ولكن الكتابة والتحميل للمشتركين المسجلين فقط'; diff --git a/inc/lang/az/lang.php b/inc/lang/az/lang.php index 384a347b7..6df15a83e 100644 --- a/inc/lang/az/lang.php +++ b/inc/lang/az/lang.php @@ -208,8 +208,8 @@ $lang['i_writeerr'] = '%s yaradıla bilmədi. Faylın/qo $lang['i_badhash'] = 'dokuwiki.php tanıla bilmir və ya dəyişdirilmişdir (hash=%s)'; $lang['i_badval'] = '%s - səhv ya boş qiymətdir'; $lang['i_success'] = 'Konfiqurasiya uğurla başa çatdı. İndi siz install.php faylını silə bilərsiniz. - Yeni DokuWiki-nizə xoş gəlmişsiniz!'; -$lang['i_failure'] = 'Konfiqurasiya fayllarına məlumat yazan zaman səhvlər tapıldı. Yəgin ki, yeni DokuWiki-nizi istifadə etmədən öncə, Siz o xətaları əl ilə düzəltməli olacaqsınız.'; + Yeni DokuWiki-nizə xoş gəlmişsiniz!'; +$lang['i_failure'] = 'Konfiqurasiya fayllarına məlumat yazan zaman səhvlər tapıldı. Yəgin ki, yeni DokuWiki-nizi istifadə etmədən öncə, Siz o xətaları əl ilə düzəltməli olacaqsınız.'; $lang['i_policy'] = 'İlkin giriş haqları siyasəti'; $lang['i_pol0'] = 'Tam açıq wiki (oxumaq, yazmaq, fayl yükləmək hamıya olar)'; $lang['i_pol1'] = 'Acıq wiki (oxumaq hamıya olar, yazmaq və fayl yükləmək ancaq üzv olan istifadəçilərə olar)'; diff --git a/inc/lang/bg/lang.php b/inc/lang/bg/lang.php index 7a9a20469..04b0edd08 100644 --- a/inc/lang/bg/lang.php +++ b/inc/lang/bg/lang.php @@ -298,10 +298,10 @@ $lang['i_confexists'] = '%s вече съществува' $lang['i_writeerr'] = '%s не можа да бъде създаден. Трябва да проверите правата за достъп до директорията/файла и да създадете файла ръчно.'; $lang['i_badhash'] = 'Файлът dokuwiki.php не може да бъде разпознат или е променен (hash=%s)'; $lang['i_badval'] = '%s - непозволена или празна стойност'; -$lang['i_success'] = 'Настройването приключи успешно. Вече можете да изтриете файла install.php. Продължете към Вашето ново DokuWiki.'; +$lang['i_success'] = 'Настройването приключи успешно. Вече можете да изтриете файла install.php. Продължете към Вашето ново DokuWiki.'; -$lang['i_failure'] = 'Възникнаха грешки при записването на файловете с настройки. Вероятно ще се наложи да ги поправите ръчно, - за да можете да ползвате Вашето ново DokuWiki.'; +$lang['i_failure'] = 'Възникнаха грешки при записването на файловете с настройки. Вероятно ще се наложи да ги поправите ръчно, + за да можете да ползвате Вашето ново DokuWiki.'; $lang['i_policy'] = 'Първоначална политика за достъп'; $lang['i_pol0'] = 'Отворено Wiki (всеки може да чете, пише и качва)'; $lang['i_pol1'] = 'Публично Wiki (всеки може да чете, само регистрирани пишат и качват)'; diff --git a/inc/lang/ca-valencia/lang.php b/inc/lang/ca-valencia/lang.php index e62010418..532f6c73d 100644 --- a/inc/lang/ca-valencia/lang.php +++ b/inc/lang/ca-valencia/lang.php @@ -211,9 +211,9 @@ $lang['i_writeerr'] = 'No es pot crear %s. Haurà de com $lang['i_badhash'] = 'dokuwiki.php substituït o modificat (hash=%s)'; $lang['i_badval'] = '%s - valor illegal o buit'; $lang['i_success'] = 'La configuració ha finalisat correctament. Ya pot borrar l\'archiu install.php. Passe al -nou DokuWiki.'; +nou DokuWiki.'; $lang['i_failure'] = 'Han aparegut alguns erros escrivint els archius de configuració. Deurà arreglar-los manualment abans de que -puga utilisar el nou DokuWiki.'; +puga utilisar el nou DokuWiki.'; $lang['i_policy'] = 'Política inicial ACL'; $lang['i_pol0'] = 'Wiki obert (llegir, escriure i enviar tots)'; $lang['i_pol1'] = 'Wiki públic (llegir tots, escriure i enviar només usuaris registrats)'; diff --git a/inc/lang/ca/lang.php b/inc/lang/ca/lang.php index 7fc078419..0fd88ec39 100644 --- a/inc/lang/ca/lang.php +++ b/inc/lang/ca/lang.php @@ -208,8 +208,8 @@ $lang['i_confexists'] = '%s ja existeix'; $lang['i_writeerr'] = 'No es pot crear %s. Comproveu els permisos del directori i/o del fitxer i creeu el fitxer manualment.'; $lang['i_badhash'] = 'dokuwiki.php no reconegut o modificat (hash=%s)'; $lang['i_badval'] = '%s - valor il·legal o buit'; -$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; -$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; +$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; +$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; $lang['i_policy'] = 'Política ACL inicial'; $lang['i_pol0'] = 'Wiki obert (tothom pot llegir, escriure i penjar fitxers)'; $lang['i_pol1'] = 'Wiki públic (tothom pot llegir, els usuaris registrats poden escriure i penjar fitxers)'; diff --git a/inc/lang/cs/lang.php b/inc/lang/cs/lang.php index 11033ff68..b5ed76bf5 100644 --- a/inc/lang/cs/lang.php +++ b/inc/lang/cs/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s již existuje'; $lang['i_writeerr'] = 'Nelze vytvořit %s. Budete muset zkontrolovat práva k souborům či adresářům a vytvořit tento soubor ručně.'; $lang['i_badhash'] = 'soubor dokuwiki.php (hash=%s) nebyl rozpoznán nebo byl upraven'; $lang['i_badval'] = '%s - neplatná nebo prázdná hodnota'; -$lang['i_success'] = 'Konfigurace byla úspěšně dokončena. Nyní můžete smazat soubor install.php. Pokračujte do své nové DokuWiki.'; -$lang['i_failure'] = 'Vyskytly se nějaké chyby při zápisu do konfiguračních souborů. Budete je nejspíš muset upravit ručně před použitím své nové DokuWiki.'; +$lang['i_success'] = 'Konfigurace byla úspěšně dokončena. Nyní můžete smazat soubor install.php. Pokračujte do své nové DokuWiki.'; +$lang['i_failure'] = 'Vyskytly se nějaké chyby při zápisu do konfiguračních souborů. Budete je nejspíš muset upravit ručně před použitím své nové DokuWiki.'; $lang['i_policy'] = 'Úvodní politika ACL'; $lang['i_pol0'] = 'Otevřená wiki (čtení, zápis a upload pro všechny)'; $lang['i_pol1'] = 'Veřejná wiki (čtení pro všechny, zápis a upload pro registrované uživatele)'; diff --git a/inc/lang/da/lang.php b/inc/lang/da/lang.php index 2c26cd3da..88cbb6871 100644 --- a/inc/lang/da/lang.php +++ b/inc/lang/da/lang.php @@ -285,8 +285,8 @@ $lang['i_confexists'] = '%s eksisterer allerede'; $lang['i_writeerr'] = 'Kunne ikke oprette %s. Du bliver nød til at tjekke mappe/fil- tilladelserne og oprette filen manuelt.'; $lang['i_badhash'] = 'uigenkendelig eller modificeret dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - ulovlig eller tom værdi'; -$lang['i_success'] = 'Konfigurationen fulførtedes med success. Du kan nu slette install.php filen. Fortsætte til din nye DokuWiki.'; -$lang['i_failure'] = 'Nogle fejl forekom mens konfigurations filerne skulle skrives. Du er mulighvis nød til at fixe dem manuelt før du kan bruge din nye DokuWiki.'; +$lang['i_success'] = 'Konfigurationen fulførtedes med success. Du kan nu slette install.php filen. Fortsætte til din nye DokuWiki.'; +$lang['i_failure'] = 'Nogle fejl forekom mens konfigurations filerne skulle skrives. Du er mulighvis nød til at fixe dem manuelt før du kan bruge din nye DokuWiki.'; $lang['i_policy'] = 'Begyndende ACL politik'; $lang['i_pol0'] = 'Åben Wiki (alle kan læse, skrive og uploade)'; $lang['i_pol1'] = 'Offentlig Wiki (alle kan læse, kun registrerede brugere kan skrive og overføre)'; diff --git a/inc/lang/de-informal/lang.php b/inc/lang/de-informal/lang.php index cce898b15..bedda77ae 100644 --- a/inc/lang/de-informal/lang.php +++ b/inc/lang/de-informal/lang.php @@ -287,8 +287,8 @@ $lang['i_confexists'] = '%s existiert bereits'; $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. Du solltest die Verzeichnis-/Datei-Rechte überprüfen und die Datei manuell anlegen.'; $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; -$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Du kannst jetzt die install.php löschen. Dein neues DokuWiki ist jetzt für dich bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese vermutlich von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; +$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Du kannst jetzt die install.php löschen. Dein neues DokuWiki ist jetzt für dich bereit.'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese vermutlich von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; $lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; $lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; $lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php index 07052fb3d..3810d4378 100644 --- a/inc/lang/de/lang.php +++ b/inc/lang/de/lang.php @@ -289,8 +289,8 @@ $lang['i_confexists'] = '%s existiert bereits'; $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. Sie sollten die Verzeichnis-/Datei-Rechte überprüfen und die Datei manuell anlegen.'; $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; -$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Sie können jetzt die install.php löschen. Ihr neues DokuWiki ist jetzt für Sie bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese vermutlich von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; +$lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Sie können jetzt die install.php löschen. Ihr neues DokuWiki ist jetzt für Sie bereit.'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese vermutlich von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; $lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; $lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; $lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; diff --git a/inc/lang/el/lang.php b/inc/lang/el/lang.php index d5bde6929..3220988ef 100644 --- a/inc/lang/el/lang.php +++ b/inc/lang/el/lang.php @@ -270,8 +270,8 @@ $lang['i_confexists'] = '%s υπάρχει ήδη'; $lang['i_writeerr'] = 'Δεν είναι δυνατή η δημιουργία του %s. Πρέπει να διορθώσετε τα δικαιώματα πρόσβασης αυτού του φακέλου/αρχείου και να δημιουργήσετε το αρχείο χειροκίνητα!'; $lang['i_badhash'] = 'Μη αναγνωρίσιμο ή τροποποιημένο αρχείο dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - λάθος ή ανύπαρκτη τιμή'; -$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; -$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; +$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; +$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; $lang['i_policy'] = 'Αρχική πολιτική Λίστας Δικαιωμάτων Πρόσβασης - ACL'; $lang['i_pol0'] = 'Ανοιχτό Wiki (όλοι μπορούν να διαβάσουν ή να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; $lang['i_pol1'] = 'Δημόσιο Wiki (όλοι μπορούν να διαβάσουν σελίδες αλλά μόνο οι εγγεγραμμένοι χρήστες μπορούν να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 73c3522fd..99877320f 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -302,9 +302,9 @@ $lang['i_writeerr'] = 'Unable to create %s. You will nee $lang['i_badhash'] = 'unrecognised or modified dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - illegal or empty value'; $lang['i_success'] = 'The configuration was finished successfully. You may delete the install.php file now. Continue to - your new DokuWiki.'; + your new DokuWiki.'; $lang['i_failure'] = 'Some errors occurred while writing the configuration files. You may need to fix them manually before - you can use your new DokuWiki.'; + you can use your new DokuWiki.'; $lang['i_policy'] = 'Initial ACL policy'; $lang['i_pol0'] = 'Open Wiki (read, write, upload for everyone)'; $lang['i_pol1'] = 'Public Wiki (read for everyone, write and upload for registered users)'; diff --git a/inc/lang/eo/lang.php b/inc/lang/eo/lang.php index 3e0d86386..ccfc85fc8 100644 --- a/inc/lang/eo/lang.php +++ b/inc/lang/eo/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s jam ekzistas'; $lang['i_writeerr'] = 'Ne eblas krei "%s". Vi bezonas kontroli la permesojn de la dosier(uj)oj kaj mem krej la dosieron.'; $lang['i_badhash'] = 'dokuwiki.php ne estas rekonebla aŭ ĝi estas modifita (hash=%s)'; $lang['i_badval'] = '%s - malvalida aŭ malplena valoro'; -$lang['i_success'] = 'La agordado sukcese kompletiĝis. Vi povas forigi la dosieron nun. Pluiru al via nova DokuWiki.'; -$lang['i_failure'] = 'Kelkaj eraroj okazis dum la konservo de la agordaj dosieroj. Vi devas senpere korekti ilin antaŭ ol vi povos uzi vian novan DokuWiki-on. '; +$lang['i_success'] = 'La agordado sukcese kompletiĝis. Vi povas forigi la dosieron nun. Pluiru al via nova DokuWiki.'; +$lang['i_failure'] = 'Kelkaj eraroj okazis dum la konservo de la agordaj dosieroj. Vi devas senpere korekti ilin antaŭ ol vi povos uzi vian novan DokuWiki-on. '; $lang['i_policy'] = 'Komenca ACL-a agordo'; $lang['i_pol0'] = 'Malferma Vikio (legi, skribi, alŝuti povas ĉiuj)'; $lang['i_pol1'] = 'Publika Vikio (legi povas ĉiuj, skribi kaj alŝuti povas registritaj uzantoj)'; diff --git a/inc/lang/es/lang.php b/inc/lang/es/lang.php index c09b12121..59a68fec4 100644 --- a/inc/lang/es/lang.php +++ b/inc/lang/es/lang.php @@ -287,8 +287,8 @@ $lang['i_confexists'] = '%s ya existe'; $lang['i_writeerr'] = 'Imposible crear %s. Se necesita que usted controle los permisos del fichero/directorio y que cree el fichero manualmente.'; $lang['i_badhash'] = 'dokuwiki.php no reconocido o modificado (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal o vacío'; -$lang['i_success'] = 'La configuración ha concluido correctamente. Ahora puede eliminar el archivo install.php. Visite su nuevo DokuWiki.'; -$lang['i_failure'] = 'Han ocurrido algunos errores durante la escritura de los ficheros de configuración. Puede ser que necesite corregirlos manualmente antes de poder usar su nuevo DokuWiki.'; +$lang['i_success'] = 'La configuración ha concluido correctamente. Ahora puede eliminar el archivo install.php. Visite su nuevo DokuWiki.'; +$lang['i_failure'] = 'Han ocurrido algunos errores durante la escritura de los ficheros de configuración. Puede ser que necesite corregirlos manualmente antes de poder usar su nuevo DokuWiki.'; $lang['i_policy'] = 'Política de ACL inicial'; $lang['i_pol0'] = 'Wiki abierto (leer, escribir y subir archivos para todos)'; $lang['i_pol1'] = 'Wiki público (leer para todos, escribir y subir archivos para usuarios registrados únicamente)'; diff --git a/inc/lang/et/lang.php b/inc/lang/et/lang.php index 37d4538ec..8ae61558a 100644 --- a/inc/lang/et/lang.php +++ b/inc/lang/et/lang.php @@ -223,8 +223,8 @@ $lang['i_permfail'] = 'Dokuwiki ei saa kirjutada faili %svotre nouveau DokuWiki.'; -$lang['i_failure'] = 'Des erreurs sont survenues lors de l\'écriture des fichiers de configuration. Il vous faudra les corriger manuellement avant de pouvoir utiliser votre nouveau DokuWiki.'; +$lang['i_success'] = 'L\'installation s\'est terminée avec succès. Vous pouvez maintenant supprimer le fichier « install.php ». Continuer avec votre nouveau DokuWiki.'; +$lang['i_failure'] = 'Des erreurs sont survenues lors de l\'écriture des fichiers de configuration. Il vous faudra les corriger manuellement avant de pouvoir utiliser votre nouveau DokuWiki.'; $lang['i_policy'] = 'Politique d\'ACL initiale'; $lang['i_pol0'] = 'Wiki ouvert (lecture, écriture, envoi de fichiers pour tout le monde)'; $lang['i_pol1'] = 'Wiki public (lecture pour tout le monde, écriture et envoi de fichiers pour les utilisateurs enregistrés)'; diff --git a/inc/lang/gl/lang.php b/inc/lang/gl/lang.php index 466f0fb8c..7c8b82691 100644 --- a/inc/lang/gl/lang.php +++ b/inc/lang/gl/lang.php @@ -269,9 +269,9 @@ $lang['i_writeerr'] = 'Non se puido crear %s. Terás de $lang['i_badhash'] = 'dokuwiki.php irrecoñecíbel ou modificado (hash=%s)'; $lang['i_badval'] = '%s - ilegal ou valor baleiro'; $lang['i_success'] = 'A configuración rematou correctamente. Agora podes eliminar o arquivo install.php. Continúa deica o - teu novo DokuWiki.'; + teu novo DokuWiki.'; $lang['i_failure'] = 'Houbo algúns erros ao tentar escribir os arquivos de configuración. Pode que precises solucionalos de xeito manual antes - de poderes empregar o teu novo DokuWiki.'; + de poderes empregar o teu novo DokuWiki.'; $lang['i_policy'] = 'Regras iniciais da ACL'; $lang['i_pol0'] = 'Wiki Aberto (lectura, escritura, subida de arquivos para todas as persoas)'; $lang['i_pol1'] = 'Wiki Público (lectura para todas as persoas, escritura e subida de arquivos para usuarios rexistrados)'; diff --git a/inc/lang/he/lang.php b/inc/lang/he/lang.php index 9cbcc3c4f..e474501ae 100644 --- a/inc/lang/he/lang.php +++ b/inc/lang/he/lang.php @@ -249,8 +249,8 @@ $lang['i_confexists'] = '%s כבר קיים'; $lang['i_writeerr'] = 'אין אפשרות ליצור את %s. נא לבדוק את הרשאות הקובץ/תיקייה וליצור את הקובץ ידנית.'; $lang['i_badhash'] = 'הקובץ Dokuwiki.php אינו מזוהה או שעבר שינויים (hash=%s)'; $lang['i_badval'] = '%s - הערך אינו חוקי או ריק'; -$lang['i_success'] = 'תהליך ההגדרה הסתיים בהצלחה. כעת ניתן למחוק את הקובץ install.php ולהמשיך אל ה־DokuWiki החדש שלך.'; -$lang['i_failure'] = 'מספר שגיאות אירעו בעת כתיבת קובצי התצורה. יתכן כי יהיה צורך לתקנם ידנית לפני שניתן יהיה להשתמש ב־DokuWiki החדש שלך.'; +$lang['i_success'] = 'תהליך ההגדרה הסתיים בהצלחה. כעת ניתן למחוק את הקובץ install.php ולהמשיך אל ה־DokuWiki החדש שלך.'; +$lang['i_failure'] = 'מספר שגיאות אירעו בעת כתיבת קובצי התצורה. יתכן כי יהיה צורך לתקנם ידנית לפני שניתן יהיה להשתמש ב־DokuWiki החדש שלך.'; $lang['i_policy'] = 'מדיניות ACL התחלתית'; $lang['i_pol0'] = 'ויקי פתוח (קריאה, כתיבה והעלאה לכולם)'; $lang['i_pol1'] = ' ויקי ציבורי (קריאה לכולם, כתיבה והעלאה למשתמשים רשומים)'; diff --git a/inc/lang/hr/lang.php b/inc/lang/hr/lang.php index ac2a740ef..97f4cf0c2 100644 --- a/inc/lang/hr/lang.php +++ b/inc/lang/hr/lang.php @@ -247,8 +247,8 @@ $lang['i_confexists'] = '%s već postoji'; $lang['i_writeerr'] = 'Ne može se kreirati %s. Trebate provjeriti dozvole direktorija/datoteke i kreirati dokument ručno.'; $lang['i_badhash'] = 'neprepoznat ili promijenjen dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - nedozvoljena ili prazna vrijednost'; -$lang['i_success'] = 'Konfiguracija je uspješno završena. Sada možete obrisati install.php datoteku. Nastavite na vaš novi DokuWiki.'; -$lang['i_failure'] = 'Pojavile su se neke greške prilikom pisanja konfiguracijskih datoteka. Morati ćete ih ručno ispraviti da bi mogli koristiti vaš novi DokuWiki.'; +$lang['i_success'] = 'Konfiguracija je uspješno završena. Sada možete obrisati install.php datoteku. Nastavite na vaš novi DokuWiki.'; +$lang['i_failure'] = 'Pojavile su se neke greške prilikom pisanja konfiguracijskih datoteka. Morati ćete ih ručno ispraviti da bi mogli koristiti vaš novi DokuWiki.'; $lang['i_policy'] = 'Inicijalna ACL politika'; $lang['i_pol0'] = 'Otvoreni Wiki (čitanje, pisanje, učitavanje za sve)'; $lang['i_pol1'] = 'Javni Wiki (čitanje za sve, pisanje i učitavanje za registrirane korisnike)'; diff --git a/inc/lang/hu/lang.php b/inc/lang/hu/lang.php index 43b79b41b..c59cace77 100644 --- a/inc/lang/hu/lang.php +++ b/inc/lang/hu/lang.php @@ -252,8 +252,8 @@ $lang['i_confexists'] = '%s már létezik.'; $lang['i_writeerr'] = 'Nem tudom ezt létrehozni: %s. Ellenőrizd a könyvtár/fájl jogosultságokat, és hozd létre az állományt kézzel.'; $lang['i_badhash'] = 'A dokuwiki.php nem felismerhető vagy módosított (hash=%s)'; $lang['i_badval'] = '%s - nem helyes vagy üres érték'; -$lang['i_success'] = 'A beállítás sikeresen befejeződött. Most már letörölhető az install.php fájl. Látogasd meg az új DokuWikidet!'; -$lang['i_failure'] = 'Hiba lépett fel a konfigurációs állományok írásakor. Ki kell javítanod kézzel, mielőtt használni kezded az új DokuWikidet.'; +$lang['i_success'] = 'A beállítás sikeresen befejeződött. Most már letörölhető az install.php fájl. Látogasd meg az új DokuWikidet!'; +$lang['i_failure'] = 'Hiba lépett fel a konfigurációs állományok írásakor. Ki kell javítanod kézzel, mielőtt használni kezded az új DokuWikidet.'; $lang['i_policy'] = 'Kezdeti hozzáférési politika'; $lang['i_pol0'] = 'Nyitott Wiki (mindenki olvashatja, írhatja, és fájlokat tölthet fel)'; $lang['i_pol1'] = 'Publikus Wiki (mindenki olvashatja, de csak regisztrált felhasználók írhatják, és tölthetnek fel fájlokat)'; diff --git a/inc/lang/ia/lang.php b/inc/lang/ia/lang.php index aff133698..a9d5c376c 100644 --- a/inc/lang/ia/lang.php +++ b/inc/lang/ia/lang.php @@ -247,9 +247,9 @@ $lang['i_writeerr'] = 'Impossibile crear %s. Tu debe ver $lang['i_badhash'] = 'dokuwiki.php non recognoscite o modificate (hash=%s)'; $lang['i_badval'] = '%s - valor vacue o invalide'; $lang['i_success'] = 'Le configuration ha succedite. Tu pote ora deler le file install.php. Continua a -tu nove DokuWiki.'; +tu nove DokuWiki.'; $lang['i_failure'] = 'Alcun errores occurreva durante le scriptura del files de configuration. Es possibile que tu debe remediar iste errores manualmente ante que -tu pote usar tu nove DokuWiki.'; +tu pote usar tu nove DokuWiki.'; $lang['i_policy'] = 'Politica de ACL interne'; $lang['i_pol0'] = 'Wiki aperte (lectura, scriptura, incargamento pro omnes)'; $lang['i_pol1'] = 'Wiki public (lectura pro omnes, scriptura e incargamento pro usatores registrate)'; diff --git a/inc/lang/id/lang.php b/inc/lang/id/lang.php index 566e8dcb0..91ed38e31 100644 --- a/inc/lang/id/lang.php +++ b/inc/lang/id/lang.php @@ -188,8 +188,8 @@ $lang['i_confexists'] = '%s sudah ada'; $lang['i_writeerr'] = 'Tidak dapat membuat %s. Anda harus memeriksa konfigurasi hak akses direktori/berkas dan membuatnya secara manual.'; $lang['i_badhash'] = 'dokuwiki.php tidak dikenal atau sudah diubah (hash=%s)'; $lang['i_badval'] = '%s - tidak valid atau belum diisi'; -$lang['i_success'] = 'Konfigurasi telah berhasil. Anda boleh menghapus berkas install.php sekarang. Lanjutkan ke DokuWiki baru Anda.'; -$lang['i_failure'] = 'Terdapat beberapa kesalahan dalam menulis berkas konfigurasi. Anda harus memperbaikinnya sendiri sebelum dapat menggunakan DokuWiki baru Anda.'; +$lang['i_success'] = 'Konfigurasi telah berhasil. Anda boleh menghapus berkas install.php sekarang. Lanjutkan ke DokuWiki baru Anda.'; +$lang['i_failure'] = 'Terdapat beberapa kesalahan dalam menulis berkas konfigurasi. Anda harus memperbaikinnya sendiri sebelum dapat menggunakan DokuWiki baru Anda.'; $lang['i_policy'] = 'Policy ACL awal'; $lang['i_pol0'] = 'Wiki Terbuka (baca, tulis, upload untuk semua orang)'; $lang['i_pol1'] = 'Wiki Publik (baca untuk semua orang, tulis dan upload untuk pengguna terdaftar)'; diff --git a/inc/lang/it/lang.php b/inc/lang/it/lang.php index 6ae864f8b..3d84c7bbb 100644 --- a/inc/lang/it/lang.php +++ b/inc/lang/it/lang.php @@ -281,8 +281,8 @@ $lang['i_confexists'] = '%s esiste già'; $lang['i_writeerr'] = 'Impossibile creare %s. E\' necessario verificare i permessi della directory o del file oppure creare il file manualmente.'; $lang['i_badhash'] = 'dokuwiki.php (hash=%s) non riconosciuto o modificato'; $lang['i_badval'] = '%s - valore vuoto o non valido'; -$lang['i_success'] = 'La configurazione è stata completata correttamente. Ora è possibile eliminare il file install.php. Poi, visita il tuo nuovo DokuWiki.'; -$lang['i_failure'] = 'Si sono verificati errori durante la scrittura dei file di configurazione. Potrebbe essere necessario correggerli manualmente prima di poter utilizzare il tuo nuovo DokuWiki.'; +$lang['i_success'] = 'La configurazione è stata completata correttamente. Ora è possibile eliminare il file install.php. Poi, visita il tuo nuovo DokuWiki.'; +$lang['i_failure'] = 'Si sono verificati errori durante la scrittura dei file di configurazione. Potrebbe essere necessario correggerli manualmente prima di poter utilizzare il tuo nuovo DokuWiki.'; $lang['i_policy'] = 'Regole di accesso iniziali'; $lang['i_pol0'] = 'Wiki Aperto (lettura, scrittura, caricamento file per tutti)'; $lang['i_pol1'] = 'Wiki Pubblico (lettura per tutti, scrittura e caricamento file per gli utenti registrati)'; diff --git a/inc/lang/ja/lang.php b/inc/lang/ja/lang.php index 93300e20e..dc6146468 100644 --- a/inc/lang/ja/lang.php +++ b/inc/lang/ja/lang.php @@ -278,8 +278,8 @@ $lang['i_confexists'] = '%s は既に存在します'; $lang['i_writeerr'] = '%s を作成できません。ディレクトリとファイルの権限を確認し、それらを手動で作成する必要があります。'; $lang['i_badhash'] = 'dokuwiki.php が認識できないか、編集されています(hash=%s)'; $lang['i_badval'] = '%s - 正しくない、もしくは値が空です'; -$lang['i_success'] = '設定ファイルは正しく作成されました。作成した DokuWikiを使用するには install.php を削除してください。'; -$lang['i_failure'] = '設定ファイルの作成中にエラーが発生しました。作成した DokuWikiを使用する前に、それらの問題を手動で修正する必要があります。'; +$lang['i_success'] = '設定ファイルは正しく作成されました。作成した DokuWikiを使用するには install.php を削除してください。'; +$lang['i_failure'] = '設定ファイルの作成中にエラーが発生しました。作成した DokuWikiを使用する前に、それらの問題を手動で修正する必要があります。'; $lang['i_policy'] = 'ACL初期設定'; $lang['i_pol0'] = 'オープン Wiki(全ての人に、閲覧・書き込み・アップロードを許可)'; $lang['i_pol1'] = 'パブリック Wiki(閲覧は全ての人が可能、書き込み・アップロードは登録ユーザーのみ)'; diff --git a/inc/lang/ko/lang.php b/inc/lang/ko/lang.php index 1a618f1a7..53d3cf58e 100644 --- a/inc/lang/ko/lang.php +++ b/inc/lang/ko/lang.php @@ -279,8 +279,8 @@ $lang['i_confexists'] = '%s(은)는 이미 존재합니다 $lang['i_writeerr'] = '%s(을)를 만들 수 없습니다. 먼저 디렉토리/파일 권한을 확인하고 파일을 수동으로 만들기 바랍니다.'; $lang['i_badhash'] = 'dokuwiki.php를 인식할 수 없거나 원본 파일이 아닙니다. (해시=%s)'; $lang['i_badval'] = '%s - 유효하지 않거나 빈 값입니다.'; -$lang['i_success'] = '환경 설정이 성공적으로 끝났습니다. 지금 install.php를 지워도 상관없습니다. 새 DokuWiki로 들어갑니다.'; -$lang['i_failure'] = '환경 설정 파일에 쓰는 도중에 오류가 발생했습니다. 새 DokuWiki를 사용하기 전에 수동으로 문제를 해결할 필요가 있습니다.'; +$lang['i_success'] = '환경 설정이 성공적으로 끝났습니다. 지금 install.php를 지워도 상관없습니다. 새 DokuWiki로 들어갑니다.'; +$lang['i_failure'] = '환경 설정 파일에 쓰는 도중에 오류가 발생했습니다. 새 DokuWiki를 사용하기 전에 수동으로 문제를 해결할 필요가 있습니다.'; $lang['i_policy'] = '초기 ACL 정책'; $lang['i_pol0'] = '열린 위키 (누구나 읽기, 쓰기, 올리기가 가능합니다.)'; $lang['i_pol1'] = '공개 위키 (누구나 읽을 수 있지만, 등록된 사용자만 쓰기와 올리기가 가능합니다.)'; diff --git a/inc/lang/la/lang.php b/inc/lang/la/lang.php index c7d654e96..77fec8362 100644 --- a/inc/lang/la/lang.php +++ b/inc/lang/la/lang.php @@ -246,8 +246,8 @@ $lang['i_confexists'] = '%s iam est.'; $lang['i_writeerr'] = '%s non creari potest. Manu illum creas.'; $lang['i_badhash'] = 'Ignotum uel mutatum dokuwiki.php (%s)'; $lang['i_badval'] = '%s non legitimum uel uacuom'; -$lang['i_success'] = 'Administratio feliciter perficitur. Delere install.php documentum potes. I ad hanc paginam ut continues.'; -$lang['i_failure'] = 'Aliqui errores dum documenta administrantur sunt. Manu onerare omnes potes priusquam tuo nouo uice uteris.'; +$lang['i_success'] = 'Administratio feliciter perficitur. Delere install.php documentum potes. I ad hanc paginam ut continues.'; +$lang['i_failure'] = 'Aliqui errores dum documenta administrantur sunt. Manu onerare omnes potes priusquam tuo nouo uice uteris.'; $lang['i_policy'] = 'ICA ratio prima'; $lang['i_pol0'] = 'Vicem aperire (omnes legere, scribere, onerare possunt)'; $lang['i_pol1'] = 'Publicus uicis (omnes legere, Sodales scribere et onerare possunt)'; diff --git a/inc/lang/lv/lang.php b/inc/lang/lv/lang.php index 67480abfc..6bdec03f3 100644 --- a/inc/lang/lv/lang.php +++ b/inc/lang/lv/lang.php @@ -264,8 +264,8 @@ $lang['i_confexists'] = '%s jau ir'; $lang['i_writeerr'] = 'Nevar izveidot %s. Jāpārbauda direktorijas/faila tiesības un fails jāizveido pašam.'; $lang['i_badhash'] = 'nepazīstams vai izmainīts dokuwiki.php fails (hash=%s)'; $lang['i_badval'] = '%s - neatļauta vai tukša vērtība'; -$lang['i_success'] = 'Konfigurēšana veiksmīgi pabeigta. Tagad vari nodzēst failu install.php. Tālāk turpini savā jaunajā DokuWiki.'; -$lang['i_failure'] = 'Rakstot konfigurācijas failu, gadījās dažas kļūmes. Pirms lieto savu jauno DokuWiki, tās varbūt jāizlabo.'; +$lang['i_success'] = 'Konfigurēšana veiksmīgi pabeigta. Tagad vari nodzēst failu install.php. Tālāk turpini savā jaunajā DokuWiki.'; +$lang['i_failure'] = 'Rakstot konfigurācijas failu, gadījās dažas kļūmes. Pirms lieto savu jauno DokuWiki, tās varbūt jāizlabo.'; $lang['i_policy'] = 'Sākotnējā ACL politika'; $lang['i_pol0'] = 'Atvērts Wiki (raksta, lasa un augšupielādē ikviens)'; $lang['i_pol1'] = 'Publisks Wiki (lasa ikviens, raksta un augšupielādē reģistrēti lietotāji)'; diff --git a/inc/lang/mk/lang.php b/inc/lang/mk/lang.php index f04c51e37..7482f2512 100644 --- a/inc/lang/mk/lang.php +++ b/inc/lang/mk/lang.php @@ -214,8 +214,8 @@ $lang['i_confexists'] = '%s веќе постои'; $lang['i_writeerr'] = 'Не може да се креира %s. Треба да ги проверите пермисиите на директориумот/датотеката и рачно да ја креирате датотеката.'; $lang['i_badhash'] = 'непозната или изменете dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - нелегална или празна вредност'; -$lang['i_success'] = 'Конфигурацијата успешно заврши. Сега можете да ја избришете датотеката install.php. Продолжете до вашето ново DokuWiki.'; -$lang['i_failure'] = 'Се појавија некои грешки при запишувањето на конфигурациските датотеки. Можеби треба да ги поравите рачно пред да можете да го користите вашето ново DokuWiki.'; +$lang['i_success'] = 'Конфигурацијата успешно заврши. Сега можете да ја избришете датотеката install.php. Продолжете до вашето ново DokuWiki.'; +$lang['i_failure'] = 'Се појавија некои грешки при запишувањето на конфигурациските датотеки. Можеби треба да ги поравите рачно пред да можете да го користите вашето ново DokuWiki.'; $lang['i_policy'] = 'Почетна ACL политика'; $lang['i_pol0'] = 'Отвори вики (читај, запиши, качи за сите)'; $lang['i_pol1'] = 'Јавно вики (читај за сите, запиши и качи за регистрирани корисници)'; diff --git a/inc/lang/mr/lang.php b/inc/lang/mr/lang.php index c6064a610..b754a3f1c 100644 --- a/inc/lang/mr/lang.php +++ b/inc/lang/mr/lang.php @@ -258,8 +258,8 @@ $lang['i_confexists'] = '%s आधीच अस्ति $lang['i_writeerr'] = '%s निर्माण करू शकलो नाही. तुम्हाला डिरेक्टरी / फाइल च्या परवानग्या तपासून स्वतःच ही फाइल बनवावी लागेल.'; $lang['i_badhash'] = 'अनाकलनीय किंवा बदललेले dokuwiki.php (hash=%s)'; $lang['i_badval'] = 'code>%s - अवैध किंवा रिकामा मजकूर.'; -$lang['i_success'] = 'व्यवस्था लावण्याचे काम यशस्वीरीत्या पार पडले. आता तुम्ही install.php डिलीट करू शकता. तुमच्या नविन डॉक्युविकि वर जा.'; -$lang['i_failure'] = 'कॉन्फिगुरेशनच्या फाइल सुरक्षित करताना काही अडचणी आल्या आहेत. तुमची नवीन डॉक्युविकि वापरण्याआधी तुम्हाला ह्या फाइल स्वतः ठीक कराव्या लागतील.'; +$lang['i_success'] = 'व्यवस्था लावण्याचे काम यशस्वीरीत्या पार पडले. आता तुम्ही install.php डिलीट करू शकता. तुमच्या नविन डॉक्युविकि वर जा.'; +$lang['i_failure'] = 'कॉन्फिगुरेशनच्या फाइल सुरक्षित करताना काही अडचणी आल्या आहेत. तुमची नवीन डॉक्युविकि वापरण्याआधी तुम्हाला ह्या फाइल स्वतः ठीक कराव्या लागतील.'; $lang['i_policy'] = 'आरंभीची ACL पॉलिसी'; $lang['i_pol0'] = 'मुक्त विकी ( सर्वांना वाचन, लेखन व अपलोड करण्याची परवानगी )'; $lang['i_pol1'] = 'सार्वजनिक विकी ( सर्वांना वाचण्याची मुभा , लेखन व अपलोडची परवानगी फक्त नोंदणीकृत सदस्यांना )'; diff --git a/inc/lang/ne/lang.php b/inc/lang/ne/lang.php index bde40a670..fa6d2f705 100644 --- a/inc/lang/ne/lang.php +++ b/inc/lang/ne/lang.php @@ -191,8 +191,8 @@ $lang['i_confexists'] = '%s पहिले देखि $lang['i_writeerr'] = '%s बनाउन असमर्थ । तपाईले डाइरेक्टरी / फाइल अनुमति जाच्नु पर्छ र फाइल आफैले बनाउनु पर्छ ।'; $lang['i_badhash'] = 'पहिचान हुन नसकेको वा परिवर्तित okuwiki.php (hash=code>%s)'; $lang['i_badval'] = '%s - अवैध वा रित्तो मान '; -$lang['i_success'] = 'स्थापना सफलरुपमा समाप्त भयो ।तपाई install.php मेट्न सक्नुहु्न्छ । तपाईको नयाँ DokuWiki निरन्तर गर्न सक्नुहुन्छ ।'; -$lang['i_failure'] = 'स्थापना समयमा केहि त्रुटि फेला पर्यो ।तपाईले आफैले यसलाई तपाईको नयाँ DokuWiki प्रयोग गर्नु अगि सच्याउनुपर्ने हुन्छ ।'; +$lang['i_success'] = 'स्थापना सफलरुपमा समाप्त भयो ।तपाई install.php मेट्न सक्नुहु्न्छ । तपाईको नयाँ DokuWiki निरन्तर गर्न सक्नुहुन्छ ।'; +$lang['i_failure'] = 'स्थापना समयमा केहि त्रुटि फेला पर्यो ।तपाईले आफैले यसलाई तपाईको नयाँ DokuWiki प्रयोग गर्नु अगि सच्याउनुपर्ने हुन्छ ।'; $lang['i_policy'] = 'सुरुको ACL निति'; $lang['i_pol0'] = 'खुल्ला विकि (पठन, लेखन , अपलोड ) सबैका लागि'; $lang['i_pol1'] = 'Public विकि (पठन सवैका लागि,लेखन र अपलोड दर्ता गरिएका प्रयपगकर्ताका लागि ) '; diff --git a/inc/lang/nl/lang.php b/inc/lang/nl/lang.php index 6d14a0e4b..44feda846 100644 --- a/inc/lang/nl/lang.php +++ b/inc/lang/nl/lang.php @@ -286,8 +286,8 @@ $lang['i_confexists'] = '%s bestaat reeds'; $lang['i_writeerr'] = 'Niet mogelijk om %s aan te maken. Controleer de directory/bestandspermissies en maak het bestand handmatig aan.'; $lang['i_badhash'] = 'Onbekende of aangepaste dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - onjuiste of lege waarde'; -$lang['i_success'] = 'De configuratie is succesvol afgerond. Je kunt nu het bestand install.php verwijderen. Ga naar je nieuwe DokuWiki.'; -$lang['i_failure'] = 'Fouten deden zich voor tijdens het schrijven naar de configuratiebestanden. Pas deze aan voor je gebruik kunt maken van je nieuwe DokuWiki.'; +$lang['i_success'] = 'De configuratie is succesvol afgerond. Je kunt nu het bestand install.php verwijderen. Ga naar je nieuwe DokuWiki.'; +$lang['i_failure'] = 'Fouten deden zich voor tijdens het schrijven naar de configuratiebestanden. Pas deze aan voor je gebruik kunt maken van je nieuwe DokuWiki.'; $lang['i_policy'] = 'Initieel ACL-beleid'; $lang['i_pol0'] = 'Open wiki (lezen, schrijven, uploaden voor iedereen)'; $lang['i_pol1'] = 'Publieke wiki (lezen voor iedereen, schrijven en uploaden voor geregistreerde gebruikers)'; diff --git a/inc/lang/no/lang.php b/inc/lang/no/lang.php index 81aa39612..92c840274 100644 --- a/inc/lang/no/lang.php +++ b/inc/lang/no/lang.php @@ -282,9 +282,9 @@ $lang['i_writeerr'] = 'Kunne ikke opprette %s. Du må sj $lang['i_badhash'] = 'ikke gjenkjent eller modifisert dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - ugyldig eller tom verdi'; $lang['i_success'] = 'Konfigurasjonen ble vellykket fullført. Du kan slette install.php filen nå. Fortsett til - din nye DokuWiki.'; + din nye DokuWiki.'; $lang['i_failure'] = 'En eller flere feil oppstod ved skriving til konfigurasjonsfilene. Du må kanskje fikse dem manuelt før - du kan bruke din nye DokuWiki.'; + du kan bruke din nye DokuWiki.'; $lang['i_policy'] = 'Innledende ACL-politikk'; $lang['i_pol0'] = 'Åpen Wiki (les, skriv og opplasting for alle)'; $lang['i_pol1'] = 'Offentlig Wiki (les for alle, skriving og opplasting bare for registrerte brukere)'; diff --git a/inc/lang/pl/lang.php b/inc/lang/pl/lang.php index 0f0381e8f..7c89a611a 100644 --- a/inc/lang/pl/lang.php +++ b/inc/lang/pl/lang.php @@ -285,8 +285,8 @@ $lang['i_confexists'] = '%s już istnieje'; $lang['i_writeerr'] = 'Nie można utworzyć %s. Sprawdź uprawnienia do katalogu lub pliku i stwórz plik ręcznie.'; $lang['i_badhash'] = 'nierozpoznany lub zmodyfikowany plik dokuwiki.php (skrót=%s)'; $lang['i_badval'] = '%s - nieprawidłowa wartość lub jej brak'; -$lang['i_success'] = 'Konfiguracja pomyślnie zakończona. Możesz teraz usunąć plik install.php. Przejdź do Twojego nowego DokuWiki.'; -$lang['i_failure'] = 'Podczas zapisu plików konfiguracyjnych wystąpiły błędy. Musisz usunąć wszystkie problemy, zanim zaczniesz korzystać z Twojego nowego DokuWiki.'; +$lang['i_success'] = 'Konfiguracja pomyślnie zakończona. Możesz teraz usunąć plik install.php. Przejdź do Twojego nowego DokuWiki.'; +$lang['i_failure'] = 'Podczas zapisu plików konfiguracyjnych wystąpiły błędy. Musisz usunąć wszystkie problemy, zanim zaczniesz korzystać z Twojego nowego DokuWiki.'; $lang['i_policy'] = 'Wstępna polityka uprawnień ACL'; $lang['i_pol0'] = 'Otwarte Wiki (odczyt, zapis i dodawanie plików dla wszystkich)'; $lang['i_pol1'] = 'Publiczne Wiki (odczyt dla wszystkich, zapis i dodawanie plików tylko dla zarejestrowanych użytkowników)'; diff --git a/inc/lang/pt-br/lang.php b/inc/lang/pt-br/lang.php index 26a73bfae..3da7e9f45 100644 --- a/inc/lang/pt-br/lang.php +++ b/inc/lang/pt-br/lang.php @@ -261,8 +261,8 @@ $lang['i_confexists'] = '%s já existe'; $lang['i_writeerr'] = 'Não foi possível criar %s. É necessário checar as permissões de arquivos/diretórios e criar o arquivo manualmente.'; $lang['i_badhash'] = 'dokuwiki.php não reconhecido ou modificado (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal ou em branco'; -$lang['i_success'] = 'A configuração terminou com sucesso. Agora você deve excluir o arquivo install.php. Conheça o seu novo DokuWiki!'; -$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita dos arquivos de configuração. É necessário corrigi-los manualmente antes de usar seu novo DokuWiki'; +$lang['i_success'] = 'A configuração terminou com sucesso. Agora você deve excluir o arquivo install.php. Conheça o seu novo DokuWiki!'; +$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita dos arquivos de configuração. É necessário corrigi-los manualmente antes de usar seu novo DokuWiki'; $lang['i_policy'] = 'Política inicial de permissões'; $lang['i_pol0'] = 'Wiki aberto (leitura, escrita e envio de arquivos por todos)'; $lang['i_pol1'] = 'Wiki público (leitura por todos, escrita e envio de arquivos por usuários registrados)'; diff --git a/inc/lang/pt/lang.php b/inc/lang/pt/lang.php index 37b3fba8c..7fec71aa2 100644 --- a/inc/lang/pt/lang.php +++ b/inc/lang/pt/lang.php @@ -268,8 +268,8 @@ $lang['i_confexists'] = '%s já existe'; $lang['i_writeerr'] = 'Não foi possível criar %s. É preciso verificar as permissões e criar o ficheiro manualmente.'; $lang['i_badhash'] = 'dokuwiki.php não é o original ou não é reconhecido (hash=%s)'; $lang['i_badval'] = '%s - valor ilegal ou vazio'; -$lang['i_success'] = 'A instalação e configuração inicial foram bem sucedidas. Pode remover o install.php. Aceda ao seu novo Wiki a correr o DokuWiki.'; -$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita nos ficheiros de configuração. Poderá ser preciso corrigi-los manualmente antes de poder aceder ao seu novo Wiki a correr o DokuWiki.'; +$lang['i_success'] = 'A instalação e configuração inicial foram bem sucedidas. Pode remover o install.php. Aceda ao seu novo Wiki a correr o DokuWiki.'; +$lang['i_failure'] = 'Ocorreram alguns erros durante a escrita nos ficheiros de configuração. Poderá ser preciso corrigi-los manualmente antes de poder aceder ao seu novo Wiki a correr o DokuWiki.'; $lang['i_policy'] = 'Politica ACL inicial'; $lang['i_pol0'] = 'Wiki Aberto (ler, escrever e carregar para todos)'; $lang['i_pol1'] = 'Wiki Público (ler para todos, escrever e carregar para utilizadores inscritos)'; diff --git a/inc/lang/ro/lang.php b/inc/lang/ro/lang.php index cb8e2ed0c..94c9d87ce 100644 --- a/inc/lang/ro/lang.php +++ b/inc/lang/ro/lang.php @@ -280,8 +280,8 @@ $lang['i_confexists'] = '%s există deja'; $lang['i_writeerr'] = 'Nu s-a putut crea %s. Trebuie să verificaţi drepturile directorului/fişierului şi să creaţi fişierul manual.'; $lang['i_badhash'] = 'dokuwiki.php nu a fost recunoscut sau a fost modificat (hash=%s)'; $lang['i_badval'] = '%s - valoare nepemisă sau neintrodusă'; -$lang['i_success'] = 'Configurarea a fost finalizată cu succes. Acum puteţi sterge fişierul install.php. Continuaţi cu your new DokuWiki.'; -$lang['i_failure'] = 'Au apărut erori la scrierea fişierelor de configurare. Va trebui să le corectaţi manual înainte de a putea folosi your new DokuWiki.'; +$lang['i_success'] = 'Configurarea a fost finalizată cu succes. Acum puteţi sterge fişierul install.php. Continuaţi cu your new DokuWiki.'; +$lang['i_failure'] = 'Au apărut erori la scrierea fişierelor de configurare. Va trebui să le corectaţi manual înainte de a putea folosi your new DokuWiki.'; $lang['i_policy'] = 'Politica ACL iniţială'; $lang['i_pol0'] = 'Wiki Deschisă (citeşte, scrie şi încarcă oricine)'; $lang['i_pol1'] = 'Wiki Deschisă (citeste oricine, scrie şi încarcă doar utilizatorul înregistrat)'; diff --git a/inc/lang/ru/lang.php b/inc/lang/ru/lang.php index 920bc907f..76dcc8428 100644 --- a/inc/lang/ru/lang.php +++ b/inc/lang/ru/lang.php @@ -291,8 +291,8 @@ $lang['i_writeerr'] = 'Не удалось создать %sсвоей новой «ДокуВики».'; +$lang['i_failure'] = 'При записи в файлы конфигурации были обнаружены ошибки. Возможно, вам придётся исправить их вручную, прежде чем вы сможете использовать свою новую «ДокуВики».'; $lang['i_policy'] = 'Исходная политика прав доступа'; $lang['i_pol0'] = 'Открытая вики (чтение, запись, закачка файлов для всех)'; $lang['i_pol1'] = 'Общедоступная вики (чтение для всех, запись и загрузка файлов для зарегистрированных пользователей)'; diff --git a/inc/lang/sk/lang.php b/inc/lang/sk/lang.php index 79a13ac35..4eddc199a 100644 --- a/inc/lang/sk/lang.php +++ b/inc/lang/sk/lang.php @@ -268,8 +268,8 @@ $lang['i_confexists'] = '%s už existuje'; $lang['i_writeerr'] = 'Nie je možné vytvoriť %s. Potrebujete skontrolovať prístupové práva pre adresár/súbor a vytvoriť ho manuálne.'; $lang['i_badhash'] = 'neznámy alebo zmenený súbor dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - nesprávna alebo žiadna hodnota'; -$lang['i_success'] = 'Konfigurácia bola úspešne ukončená. Teraz môžete zmazať súbor install.php. Pokračujte vo vašej novej DokuWiki.'; -$lang['i_failure'] = 'Pri zápise konfiguračného súboru nastali nejaké chyby. Potrebujete ich opraviť manuálne pred tým, ako budete môcť používať vašu novú DokuWiki.'; +$lang['i_success'] = 'Konfigurácia bola úspešne ukončená. Teraz môžete zmazať súbor install.php. Pokračujte vo vašej novej DokuWiki.'; +$lang['i_failure'] = 'Pri zápise konfiguračného súboru nastali nejaké chyby. Potrebujete ich opraviť manuálne pred tým, ako budete môcť používať vašu novú DokuWiki.'; $lang['i_policy'] = 'Počiatočná ACL politika'; $lang['i_pol0'] = 'Otvorená Wiki (čítanie, zápis a nahrávanie pre každého)'; $lang['i_pol1'] = 'Verejná Wiki (čítanie pre každého, zápis a nahrávanie pre registrovaných užívateľov)'; diff --git a/inc/lang/sl/lang.php b/inc/lang/sl/lang.php index 7cd16f7ed..d2a8287a1 100644 --- a/inc/lang/sl/lang.php +++ b/inc/lang/sl/lang.php @@ -271,8 +271,8 @@ $lang['i_confexists'] = 'Predmet %s že obstaja.'; $lang['i_writeerr'] = 'Ni mogoče ustvariti predmeta %s. Preveriti je treba dovoljenja datotek in map in nato ustvariti datoteko ročno.'; $lang['i_badhash'] = 'nepoznana ali spremenjena datoteka dokuwiki.php (razpršilo=%s)'; $lang['i_badval'] = '%s - neveljavna ali prazna vrednost'; -$lang['i_success'] = 'Nastavitev je uspešno končana. Datoteko install.php lahko sedaj izbrišete. Nadaljujte v novi DokuWiki.'; -$lang['i_failure'] = 'Med zapisovanjem nastavitvenih datotek je prišlo do napak. Preden lahko uporabite vaš DokuWiki, jih je treba odpraviti.'; +$lang['i_success'] = 'Nastavitev je uspešno končana. Datoteko install.php lahko sedaj izbrišete. Nadaljujte v novi DokuWiki.'; +$lang['i_failure'] = 'Med zapisovanjem nastavitvenih datotek je prišlo do napak. Preden lahko uporabite vaš DokuWiki, jih je treba odpraviti.'; $lang['i_policy'] = 'Začetna določila ACL'; $lang['i_pol0'] = 'Odprt Wiki (branje, zapis, nalaganje datotek je javno za vse)'; $lang['i_pol1'] = 'Javni Wiki (branje za vse, zapis in nalaganje datotek za prijavljene uporabnike)'; diff --git a/inc/lang/sq/lang.php b/inc/lang/sq/lang.php index 3c58d708d..e190d8404 100644 --- a/inc/lang/sq/lang.php +++ b/inc/lang/sq/lang.php @@ -223,8 +223,8 @@ $lang['i_confexists'] = '%s ekziston njëherë'; $lang['i_writeerr'] = '%s nuk mundi të krijohej. Duhet të kontrolloni lejet e dirkektorisë/skedarit dhe ta krijoni skedarin manualisht.'; $lang['i_badhash'] = 'dokuwiki.php e panjohur ose e ndryshuar (hash=code>%s)'; $lang['i_badval'] = '%s - vlerë e palejuar ose boshe'; -$lang['i_success'] = 'Konfigurimi u mbarua me sukses. Tani mund ta fshini skedarin install.php. Vazhdoni tek DokuWiki juaj i ri..'; -$lang['i_failure'] = 'Ndodhën disa gabime gjatë shkrimit të skedarit të konfigurimit. Do t\'ju duhet t\'i rregulloni manualisht para se të përdorni DokuWiki-in tuaj të ri..'; +$lang['i_success'] = 'Konfigurimi u mbarua me sukses. Tani mund ta fshini skedarin install.php. Vazhdoni tek DokuWiki juaj i ri..'; +$lang['i_failure'] = 'Ndodhën disa gabime gjatë shkrimit të skedarit të konfigurimit. Do t\'ju duhet t\'i rregulloni manualisht para se të përdorni DokuWiki-in tuaj të ri..'; $lang['i_policy'] = 'Veprimi fillestar ACL'; $lang['i_pol0'] = 'Wiki i Hapur (lexim, shkrim, ngarkim për këdo)'; $lang['i_pol1'] = 'Wiki Publike (lexim për këdo, shkrim dhe ngarkim për përdoruesit e regjistruar)'; diff --git a/inc/lang/sr/lang.php b/inc/lang/sr/lang.php index 875b1d695..d7f594511 100644 --- a/inc/lang/sr/lang.php +++ b/inc/lang/sr/lang.php @@ -244,8 +244,8 @@ $lang['i_confexists'] = '%s већ постоји'; $lang['i_writeerr'] = 'Не могу да направим %s. Проверите дозволе а затим ручно направите ову датотеку.'; $lang['i_badhash'] = 'dokuwiki.php није препознат или је измењен (hash=%s)'; $lang['i_badval'] = '%s - недозвољена или празна вредност'; -$lang['i_success'] = 'Подешавања су завршена. Сада можете обрисати датотеку install.php. Наставите у Ваш нови DokuWiki.'; -$lang['i_failure'] = 'Појавили су се проблеми при писању датотеке са подешавањима. Требало би да их ручно исправите пре него што ћете моћи да користите Ваш нови DokuWiki.'; +$lang['i_success'] = 'Подешавања су завршена. Сада можете обрисати датотеку install.php. Наставите у Ваш нови DokuWiki.'; +$lang['i_failure'] = 'Појавили су се проблеми при писању датотеке са подешавањима. Требало би да их ручно исправите пре него што ћете моћи да користите Ваш нови DokuWiki.'; $lang['i_policy'] = 'Иницијалне корисничке дозволе'; $lang['i_pol0'] = 'Отворени вики (читање, писање, слање датотека за све)'; $lang['i_pol1'] = 'Јавни вики (читање за све, писање и слање датотека само за регистроване кориснике)'; diff --git a/inc/lang/sv/lang.php b/inc/lang/sv/lang.php index 4cc97e8c6..1c3141da7 100644 --- a/inc/lang/sv/lang.php +++ b/inc/lang/sv/lang.php @@ -246,9 +246,9 @@ $lang['i_writeerr'] = 'Kan inte skapa %s. Kontrollera fi $lang['i_badhash'] = 'okänd eller ändrad dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - felaktig eller blank'; $lang['i_success'] = 'Konfigurationen avslutades utan fel. Du kan radera filen install.php nu. Fortsätt till - din nya DokuWiki.'; + din nya DokuWiki.'; $lang['i_failure'] = 'Fel uppstod vid skrivning av konfigurationsfilerna. Du kan behöva ordna till dem manuellt innan - du kan använda din nya DokuWiki.'; + du kan använda din nya DokuWiki.'; $lang['i_policy'] = 'Initial ACL-policy'; $lang['i_pol0'] = 'Öppen wiki (alla får läsa, skriva och ladda upp filer)'; $lang['i_pol1'] = 'Publik wiki (alla får läsa, registrerade användare för skriva och ladda upp filer)'; diff --git a/inc/lang/tr/lang.php b/inc/lang/tr/lang.php index cf75aac9d..a8d8c5ac9 100644 --- a/inc/lang/tr/lang.php +++ b/inc/lang/tr/lang.php @@ -225,8 +225,8 @@ $lang['i_confexists'] = '%s zaten var'; $lang['i_writeerr'] = '%s oluşturulamadı. Dosya/Klasör izin ayarlarını gözden geçirip dosyayı elle oluşturmalısınız.'; $lang['i_badhash'] = 'dokuwiki.php tanınamadı ya da değiştirilmiş (hash=%s)'; $lang['i_badval'] = '%s - Yanlış veya boş değer'; -$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; -$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; +$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; +$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; $lang['i_policy'] = 'İlk ACL ayarı'; $lang['i_pol0'] = 'Tamamen Açık Wiki (herkes okuyabilir, yazabilir ve dosya yükleyebilir)'; $lang['i_pol1'] = 'Açık Wiki (herkes okuyabilir, ancak sadece üye olanlar yazabilir ve dosya yükleyebilir)'; diff --git a/inc/lang/uk/lang.php b/inc/lang/uk/lang.php index e38db2300..5274a4210 100644 --- a/inc/lang/uk/lang.php +++ b/inc/lang/uk/lang.php @@ -253,9 +253,9 @@ $lang['i_writeerr'] = 'Неможливо створити %s%s)'; $lang['i_badval'] = '%s - невірне або пусте значення.'; $lang['i_success'] = 'Налаштування завершено. Ви можете знищити файл install.php. -Перейдіть до вашої нової ДокуВікі'; +Перейдіть до вашої нової ДокуВікі'; $lang['i_failure'] = 'При збереженні файлу конфігурації виникли помилки. Можливо вам доведеться виправити їх самостійно -до початку використання вашої нової ДокуВікі.'; +до початку використання вашої нової ДокуВікі.'; $lang['i_policy'] = 'Початкова політика ACL'; $lang['i_pol0'] = 'Відкрита Вікі (читання, запис та завантаження файлів для всіх)'; $lang['i_pol1'] = 'Публічна Вікі (читання для всіх, запис та завантаження для зареєстрованих користувачів)'; diff --git a/inc/lang/zh-tw/lang.php b/inc/lang/zh-tw/lang.php index 41c79f57c..1304759f7 100644 --- a/inc/lang/zh-tw/lang.php +++ b/inc/lang/zh-tw/lang.php @@ -272,8 +272,8 @@ $lang['i_writeerr'] = '無法建立 %s。您必須檢查 $lang['i_badhash'] = '無法辨識或被變更的 dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - 非法或空白的值'; $lang['i_success'] = '設定已成功完成。您現在可以刪除 install.php 檔案。繼續到 -您的新 DokuWiki.'; -$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; +您的新 DokuWiki.'; +$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; $lang['i_policy'] = '初步的 ACL 政策'; $lang['i_pol0'] = '開放的維基 (任何人可讀取、寫入、上傳)'; $lang['i_pol1'] = '公開的維基 (任何人可讀取,註冊使用者可寫入與上傳)'; diff --git a/inc/lang/zh/lang.php b/inc/lang/zh/lang.php index 8b9644262..4315f780b 100644 --- a/inc/lang/zh/lang.php +++ b/inc/lang/zh/lang.php @@ -288,8 +288,8 @@ $lang['i_writeerr'] = '无法创建 %s。您需要检查 $lang['i_badhash'] = '无法识别的或被修改的 dokuwiki.php(值=%s)'; $lang['i_badval'] = '%s - 非法或空值'; $lang['i_success'] = '配置成功完成。您现在可以删除 install.php 了。继续进入 - 您全新的 DokuWiki。'; -$lang['i_failure'] = '写入配置文件的时候产生一些错误。在使用 您全新安装的 DokuWiki 前 + 您全新的 DokuWiki。'; +$lang['i_failure'] = '写入配置文件的时候产生一些错误。在使用 您全新安装的 DokuWiki 前 您需要手动修复它们。'; $lang['i_policy'] = '初始的 ACL 政策'; $lang['i_pol0'] = '开放的维基(任何人都有读、写、上传的权限)'; -- cgit v1.2.3 From 3755fc25fd2934f8d887f713a48b342ef252cfee Mon Sep 17 00:00:00 2001 From: Tom N Harris Date: Mon, 14 Mar 2011 20:39:30 -0400 Subject: Combine subsequent calls to strtr into a single transformation --- inc/pageutils.php | 5 ++--- inc/parser/renderer.php | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/pageutils.php b/inc/pageutils.php index ef82eebcd..ca4936a82 100644 --- a/inc/pageutils.php +++ b/inc/pageutils.php @@ -115,11 +115,10 @@ function cleanID($raw_id,$ascii=false,$media=false){ $id = utf8_strtolower($id); //alternative namespace seperator - $id = strtr($id,';',':'); if($conf['useslash']){ - $id = strtr($id,'/',':'); + $id = strtr($id,';/','::'); }else{ - $id = strtr($id,'/',$sepchar); + $id = strtr($id,';/',':'.$sepchar); } if($conf['deaccent'] == 2 || $ascii) $id = utf8_romanize($id); diff --git a/inc/parser/renderer.php b/inc/parser/renderer.php index 2c78f220a..7df369478 100644 --- a/inc/parser/renderer.php +++ b/inc/parser/renderer.php @@ -274,9 +274,10 @@ class Doku_Renderer extends DokuWiki_Plugin { list($name,$hash) = explode('#',$name,2); if($hash) return $hash; - $name = strtr($name,';',':'); if($conf['useslash']){ - $name = strtr($name,'/',':'); + $name = strtr($name,';/',';:'); + }else{ + $name = strtr($name,';',':'); } return noNSorNS($name); -- cgit v1.2.3 From 78f0e83246184ff6cb8679d086b0fa17ab508057 Mon Sep 17 00:00:00 2001 From: Leandro Regueiro Date: Fri, 23 Nov 2012 09:23:29 +0100 Subject: Galician language update --- inc/lang/gl/lang.php | 15 +++++++++++++-- inc/lang/gl/mailwrap.html | 13 +++++++++++++ inc/lang/gl/resetpwd.txt | 3 +++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 inc/lang/gl/mailwrap.html create mode 100644 inc/lang/gl/resetpwd.txt (limited to 'inc') diff --git a/inc/lang/gl/lang.php b/inc/lang/gl/lang.php index 45fd444d9..a33aba72e 100644 --- a/inc/lang/gl/lang.php +++ b/inc/lang/gl/lang.php @@ -5,6 +5,7 @@ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Medúlio * @author Oscar M. Lage + * @author Leandro Regueiro */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -40,6 +41,7 @@ $lang['btn_backtomedia'] = 'Volver á Selección de Arquivos-Media'; $lang['btn_subscribe'] = 'Avísame dos trocos na páxina'; $lang['btn_profile'] = 'Actualizar Perfil'; $lang['btn_reset'] = 'Reiniciar'; +$lang['btn_resendpwd'] = 'Definir novo contrasinal'; $lang['btn_draft'] = 'Editar borrador'; $lang['btn_recover'] = 'Recuperar borrador'; $lang['btn_draftdel'] = 'Eliminar borrador'; @@ -76,6 +78,7 @@ $lang['profnoempty'] = 'Non se permite un nome ou un enderezo de corre $lang['profchanged'] = 'Perfil de usuario actualizado correctamente.'; $lang['pwdforget'] = 'Esqueceches o teu contrasinal? Consegue un novo'; $lang['resendna'] = 'Este wiki non permite o reenvío de contrasinais.'; +$lang['resendpwd'] = 'Definir novo contrasinal para'; $lang['resendpwdmissing'] = 'Sentímolo, tes que cubrir todos os campos.'; $lang['resendpwdnouser'] = 'Sentímolo, non atopamos este usuario no noso banco de datos.'; $lang['resendpwdbadauth'] = 'Sentímolo, mais este código de autorización non é válido. Asegúrate de que usaches a ligazón completa de confirmación.'; @@ -88,6 +91,7 @@ $lang['searchmedia_in'] = 'Procurar en %s'; $lang['txt_upload'] = 'Escolle o arquivo para subir'; $lang['txt_filename'] = 'Subir como (opcional)'; $lang['txt_overwrt'] = 'Sobrescribir arquivo existente'; +$lang['maxuploadsize'] = 'Envío máx. de %s por ficheiro.'; $lang['lockedby'] = 'Bloqueado actualmente por'; $lang['lockexpire'] = 'O bloqueo remata o'; $lang['js']['willexpire'] = 'O teu bloqueo para editares esta páxina vai caducar nun minuto.\nPara de evitar conflitos, emprega o botón de previsualización para reiniciares o contador do tempo de bloqueo.'; @@ -182,6 +186,12 @@ $lang['external_edit'] = 'edición externa'; $lang['summary'] = 'Resumo da edición'; $lang['noflash'] = 'Precísase o Extensión Adobe Flash para amosar este contido.'; $lang['download'] = 'Descargar Retallo (Snippet)'; +$lang['tools'] = 'Ferramentas'; +$lang['user_tools'] = 'Ferramentas de usuario'; +$lang['site_tools'] = 'Ferramentas do sitio'; +$lang['page_tools'] = 'Ferramentas de páxina'; +$lang['skip_to_content'] = 'saltar ao contido'; +$lang['sidebar'] = 'Barra lateral'; $lang['mail_newpage'] = 'páxina engadida:'; $lang['mail_changed'] = 'páxina mudada:'; $lang['mail_subscribe_list'] = 'páxinas mudadas en nome de espazo:'; @@ -252,6 +262,7 @@ $lang['subscr_style_digest'] = 'correo-e con resumo de trocos para cada páxin $lang['subscr_style_list'] = 'lista de páxinas mudadas dende o último correo-e'; $lang['authmodfailed'] = 'Configuración de autenticación de usuario incorrecta. Por favor, informa ao Administrador do teu Wiki.'; $lang['authtempfail'] = 'A autenticación de usuario non está dispoñible de xeito temporal. De persistir esta situación, por favor, informa ao Administrador do teu Wiki.'; +$lang['authpwdexpire'] = 'O seu contrasinal vai caducar en %d días, razón pola cal debería cambialo axiña.'; $lang['i_chooselang'] = 'Escolle o teu idioma'; $lang['i_installer'] = 'Instalador do DokuWiki'; $lang['i_wikiname'] = 'Nome do Wiki'; @@ -269,9 +280,9 @@ $lang['i_writeerr'] = 'Non se puido crear %s. Terás de $lang['i_badhash'] = 'dokuwiki.php irrecoñecíbel ou modificado (hash=%s)'; $lang['i_badval'] = '%s - ilegal ou valor baleiro'; $lang['i_success'] = 'A configuración rematou correctamente. Agora podes eliminar o arquivo install.php. Continúa deica o - teu novo DokuWiki.'; + teu novo DokuWiki.'; $lang['i_failure'] = 'Houbo algúns erros ao tentar escribir os arquivos de configuración. Pode que precises solucionalos de xeito manual antes - de poderes empregar o teu novo DokuWiki.'; + de poderes empregar o teu novo DokuWiki.'; $lang['i_policy'] = 'Regras iniciais da ACL'; $lang['i_pol0'] = 'Wiki Aberto (lectura, escritura, subida de arquivos para todas as persoas)'; $lang['i_pol1'] = 'Wiki Público (lectura para todas as persoas, escritura e subida de arquivos para usuarios rexistrados)'; diff --git a/inc/lang/gl/mailwrap.html b/inc/lang/gl/mailwrap.html new file mode 100644 index 000000000..05ef4175a --- /dev/null +++ b/inc/lang/gl/mailwrap.html @@ -0,0 +1,13 @@ + + +@TITLE@ + + + + +@HTMLBODY@ + +

+Esta mensaxe foi xerada por DokuWiki en @DOKUWIKIURL@. + + \ No newline at end of file diff --git a/inc/lang/gl/resetpwd.txt b/inc/lang/gl/resetpwd.txt new file mode 100644 index 000000000..7b662d8f5 --- /dev/null +++ b/inc/lang/gl/resetpwd.txt @@ -0,0 +1,3 @@ +====== Definir novo contrasinal ====== + +Introduza un novo contrasinal para a súa conta neste wiki. \ No newline at end of file -- cgit v1.2.3 From 1f61f312a78e212aaa4a4bc3f60961036ec07fd7 Mon Sep 17 00:00:00 2001 From: Dominik Eckelmann Date: Fri, 23 Nov 2012 18:48:09 +0100 Subject: remove empty BCC/CC mail headers Empty BCC/CC headers may cause errors on IIS. --- inc/Mailer.class.php | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'inc') diff --git a/inc/Mailer.class.php b/inc/Mailer.class.php index cbd1eb0a9..256a76d22 100644 --- a/inc/Mailer.class.php +++ b/inc/Mailer.class.php @@ -553,6 +553,7 @@ class Mailer { * @returns string the headers */ protected function prepareHeaders() { + $this->removeEmptyBccOrCcHeader(); $headers = ''; foreach($this->headers as $key => $val) { $headers .= "$key: $val".MAILHEADER_EOL; @@ -560,6 +561,16 @@ class Mailer { return $headers; } + /** + * Removes empty BCC and CC Header. + * + * Empty BCC/CC Header can cause an error with Microsoft IIS. + */ + protected function removeEmptyBccOrCcHeader() { + if (isset($this->headers['Bcc']) && empty($this->headers['Bcc'])) unset($this->headers['Bcc']); + if (isset($this->headers['Cc']) && empty($this->headers['Cc'])) unset($this->headers['Cc']); + } + /** * return a full email with all headers * -- cgit v1.2.3 From 91effd8de07e4afc59b6763ac72b268f58cfc941 Mon Sep 17 00:00:00 2001 From: Dominik Eckelmann Date: Fri, 23 Nov 2012 19:55:44 +0100 Subject: ignore empty header on mail sending --- inc/Mailer.class.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'inc') diff --git a/inc/Mailer.class.php b/inc/Mailer.class.php index 256a76d22..f1492be9b 100644 --- a/inc/Mailer.class.php +++ b/inc/Mailer.class.php @@ -553,24 +553,14 @@ class Mailer { * @returns string the headers */ protected function prepareHeaders() { - $this->removeEmptyBccOrCcHeader(); $headers = ''; foreach($this->headers as $key => $val) { + if ($val === '') continue; $headers .= "$key: $val".MAILHEADER_EOL; } return $headers; } - /** - * Removes empty BCC and CC Header. - * - * Empty BCC/CC Header can cause an error with Microsoft IIS. - */ - protected function removeEmptyBccOrCcHeader() { - if (isset($this->headers['Bcc']) && empty($this->headers['Bcc'])) unset($this->headers['Bcc']); - if (isset($this->headers['Cc']) && empty($this->headers['Cc'])) unset($this->headers['Cc']); - } - /** * return a full email with all headers * -- cgit v1.2.3 From b7183bb542c52e0826a76346fd9005da965a9ac5 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 24 Nov 2012 08:23:32 +0100 Subject: GeSHi update to 1.0.8.11 --- inc/geshi.php | 68 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 24 deletions(-) (limited to 'inc') diff --git a/inc/geshi.php b/inc/geshi.php index aedc64f84..c6ff9ef77 100644 --- a/inc/geshi.php +++ b/inc/geshi.php @@ -41,7 +41,7 @@ // /** The version of this GeSHi file */ -define('GESHI_VERSION', '1.0.8.10'); +define('GESHI_VERSION', '1.0.8.11'); // Define the root directory for the GeSHi code tree if (!defined('GESHI_ROOT')) { @@ -604,6 +604,17 @@ class GeSHi { $this->set_language_path($path); } + /** + * Returns the version of GeSHi + * + * @return string + * @since 1 0.8.11 + */ + function get_version() + { + return GESHI_VERSION; + } + /** * Returns an error message associated with the last GeSHi operation, * or false if no error has occured @@ -808,7 +819,7 @@ class GeSHi { } // match the langname - if (!preg_match('/\'LANG_NAME\'\s*=>\s*\'((?:[^\']|\\\')+)\'/', $data, $matches)) { + if (!preg_match('/\'LANG_NAME\'\s*=>\s*\'((?:[^\']|\\\')+?)\'/', $data, $matches)) { $this->error = sprintf('Geshi::get_lang_fullname(%s): Regex can not detect language', $language); return false; } @@ -1437,6 +1448,8 @@ class GeSHi { * @todo static? */ function get_language_name_from_extension( $extension, $lookup = array() ) { + $extension = strtolower($extension); + if ( !is_array($lookup) || empty($lookup)) { $lookup = array( '6502acme' => array( 'a', 's', 'asm', 'inc' ), @@ -1470,6 +1483,7 @@ class GeSHi { 'gnuplot' => array('plt'), 'groovy' => array('groovy'), 'haskell' => array('hs'), + 'haxe' => array('hx'), 'html4strict' => array('html', 'htm'), 'ini' => array('ini', 'desktop'), 'java' => array('java'), @@ -1504,6 +1518,7 @@ class GeSHi { 'smalltalk' => array('st'), 'smarty' => array(), 'tcl' => array('tcl'), + 'text' => array('txt'), 'vb' => array('bas'), 'vbnet' => array(), 'visualfoxpro' => array(), @@ -1518,7 +1533,8 @@ class GeSHi { return $lang; } } - return ''; + + return 'text'; } /** @@ -1555,6 +1571,9 @@ class GeSHi { * @since 1.0.0 */ function add_keyword($key, $word) { + if (!is_array($this->language_data['KEYWORDS'][$key])) { + $this->language_data['KEYWORDS'][$key] = array(); + } if (!in_array($word, $this->language_data['KEYWORDS'][$key])) { $this->language_data['KEYWORDS'][$key][] = $word; @@ -1816,7 +1835,7 @@ class GeSHi { //Decide on which style to use if ($style === null) { //Check if we should use default style unset($this->highlight_extra_lines_styles[$lines]); - } else if ($style === false) { //Check if to remove this line + } elseif ($style === false) { //Check if to remove this line unset($this->highlight_extra_lines[$lines]); unset($this->highlight_extra_lines_styles[$lines]); } else { @@ -1988,7 +2007,7 @@ class GeSHi { $this->language_data['SYMBOL_DATA'][$symbols] = 0; if (isset($symbols[1])) { // multiple chars $symbol_preg_multi[] = preg_quote($symbols, '/'); - } else if ($symbols == '-') { + } elseif ($symbols == '-') { // don't trigger range out of order error $symbol_preg_single[] = '\-'; } else { // single char @@ -2392,7 +2411,7 @@ class GeSHi { foreach ($this->language_data['QUOTEMARKS'] as $quotemark) { if (!isset($is_string_starter[$quotemark[0]])) { $is_string_starter[$quotemark[0]] = (string)$quotemark; - } else if (is_string($is_string_starter[$quotemark[0]])) { + } elseif (is_string($is_string_starter[$quotemark[0]])) { $is_string_starter[$quotemark[0]] = array( $is_string_starter[$quotemark[0]], $quotemark); @@ -2478,7 +2497,7 @@ class GeSHi { continue; } $match_i = $comment_regexp_cache_per_key[$comment_key]['pos']; - } else if ( + } elseif ( //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $i), $match, PREG_OFFSET_CAPTURE)) || (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $i)) @@ -2586,7 +2605,7 @@ class GeSHi { continue; } $match_i = $escape_regexp_cache_per_key[$escape_key]['pos']; - } else if ( + } elseif ( //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $start), $match, PREG_OFFSET_CAPTURE)) || (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $start)) @@ -2656,13 +2675,13 @@ class GeSHi { // don't put a newline around newlines $string .= "\n"; $start = $es_pos + 2; - } else if (ord($es_char) >= 128) { + } elseif (ord($es_char) >= 128) { //This is an non-ASCII char (UTF8 or single byte) //This code tries to work around SF#2037598 ... if(function_exists('mb_substr')) { $es_char_m = mb_substr(substr($part, $es_pos+1, 16), 0, 1, $this->encoding); $string .= $es_char_m . ''; - } else if (!GESHI_PHP_PRE_433 && 'utf-8' == $this->encoding) { + } elseif (!GESHI_PHP_PRE_433 && 'utf-8' == $this->encoding) { if(preg_match("/[\xC2-\xDF][\x80-\xBF]". "|\xE0[\xA0-\xBF][\x80-\xBF]". "|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}". @@ -2684,7 +2703,7 @@ class GeSHi { $string .= $this->hsc($es_char) . ''; $start = $es_pos + 2; } - } else if ($next_escape_regexp_pos < $length && + } elseif ($next_escape_regexp_pos < $length && $next_escape_regexp_pos < $close_pos) { $es_pos = $next_escape_regexp_pos; //Add the stuff not in the string yet ... @@ -2728,7 +2747,7 @@ class GeSHi { $string = ''; $i = $start - 1; continue; - } else if ($this->lexic_permissions['STRINGS'] && $hq && $hq[0] == $char && + } elseif ($this->lexic_permissions['STRINGS'] && $hq && $hq[0] == $char && substr($part, $i, $hq_strlen) == $hq && ($i != $next_comment_regexp_pos)) { // The start of a hard quoted string if (!$this->use_classes) { @@ -2886,7 +2905,7 @@ class GeSHi { continue; } $match_i = $comment_multi_cache_per_key[$open]; - } else if (($match_i = stripos($part, $open, $i)) !== false) { + } elseif (($match_i = stripos($part, $open, $i)) !== false) { $comment_multi_cache_per_key[$open] = $match_i; } else { $comment_multi_cache_per_key[$open] = false; @@ -2983,7 +3002,7 @@ class GeSHi { continue; } $match_i = $comment_single_cache_per_key[$comment_key]; - } else if ( + } elseif ( // case sensitive comments ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] && ($match_i = stripos($part, $comment_mark, $i)) !== false) || @@ -3140,10 +3159,10 @@ class GeSHi { $IN_TAG = false; } $lines[$key] .= $char; - } else if ('<' == $char) { + } elseif ('<' == $char) { $IN_TAG = true; $lines[$key] .= '<'; - } else if ('&' == $char) { + } elseif ('&' == $char) { $substr = substr($line, $i + 3, 5); $posi = strpos($substr, ';'); if (false === $posi) { @@ -3152,7 +3171,7 @@ class GeSHi { $pos -= $posi+2; } $lines[$key] .= $char; - } else if ("\t" == $char) { + } elseif ("\t" == $char) { $str = ''; // OPTIMISE - move $strs out. Make an array: // $tabs = array( @@ -3173,7 +3192,7 @@ class GeSHi { $lines[$key] .= substr($line, $i + 1); break; } - } else if (0 == $pos && ' ' == $char) { + } elseif (0 == $pos && ' ' == $char) { $lines[$key] .= ' '; ++$pos; } else { @@ -3231,6 +3250,7 @@ class GeSHi { function handle_keyword_replace($match) { $k = $this->_kw_replace_group; $keyword = $match[0]; + $keyword_match = $match[1]; $before = ''; $after = ''; @@ -3248,12 +3268,12 @@ class GeSHi { if (!$this->language_data['CASE_SENSITIVE'][$k] && strpos($this->language_data['URLS'][$k], '{FNAME}') !== false) { foreach ($this->language_data['KEYWORDS'][$k] as $word) { - if (strcasecmp($word, $keyword) == 0) { + if (strcasecmp($word, $keyword_match) == 0) { break; } } } else { - $word = $keyword; + $word = $keyword_match; } $before = '<|UR1|"' . @@ -3367,7 +3387,7 @@ class GeSHi { foreach (array_keys($this->language_data['KEYWORDS']) as $k) { if (!isset($this->lexic_permissions['KEYWORDS'][$k]) || - $this->lexic_permissions['KEYWORDS'][$k]) { + $this->lexic_permissions['KEYWORDS'][$k]) { $case_sensitive = $this->language_data['CASE_SENSITIVE'][$k]; $modifiers = $case_sensitive ? '' : 'i'; @@ -3991,7 +4011,7 @@ class GeSHi { $parsed_code .= $this->line_numbers_start + $i; if ($close) { $parsed_code .= str_repeat('', $close); - } else if ($i != $n) { + } elseif ($i != $n) { $parsed_code .= "\n"; } } @@ -4123,10 +4143,10 @@ class GeSHi { if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { if ($this->header_type == GESHI_HEADER_PRE) { return "$header"; - } else if ($this->header_type == GESHI_HEADER_DIV || + } elseif ($this->header_type == GESHI_HEADER_DIV || $this->header_type == GESHI_HEADER_PRE_VALID) { return "$header"; - } else if ($this->header_type == GESHI_HEADER_PRE_TABLE) { + } elseif ($this->header_type == GESHI_HEADER_PRE_TABLE) { return "$header"; } } else { -- cgit v1.2.3 From eb3ce0d55290dd4a60193e680aa50b46571350bd Mon Sep 17 00:00:00 2001 From: Kazutaka Miyasaka Date: Sun, 25 Nov 2012 21:02:32 +0900 Subject: Fix case-insensitive match in ACL checking ACL checking of DokuWiki is currently always case-sensitive regardless of auth backend setting ($auth->isCaseSensitive). This commit enables case-insensitive match in the same way of auth_isMember(). --- inc/auth.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/auth.php b/inc/auth.php index 1c8a8f5f5..9c458338d 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -535,9 +535,10 @@ function auth_aclcheck($id, $user, $groups) { return AUTH_ADMIN; } - $ci = ''; - if(!$auth->isCaseSensitive()) $ci = 'ui'; - + if(!$auth->isCaseSensitive()) { + $user = utf8_strtolower($user); + $groups = array_map('utf8_strtolower', $groups); + } $user = $auth->cleanUser($user); $groups = array_map(array($auth, 'cleanGroup'), (array) $groups); $user = auth_nameencode($user); @@ -561,11 +562,14 @@ function auth_aclcheck($id, $user, $groups) { } //check exact match first - $matches = preg_grep('/^'.preg_quote($id, '/').'\s+(\S+)\s+/'.$ci, $AUTH_ACL); + $matches = preg_grep('/^'.preg_quote($id, '/').'\s+(\S+)\s+/u', $AUTH_ACL); if(count($matches)) { foreach($matches as $match) { $match = preg_replace('/#.*$/', '', $match); //ignore comments $acl = preg_split('/\s+/', $match); + if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') { + $acl[1] = utf8_strtolower($acl[1]); + } if(!in_array($acl[1], $groups)) { continue; } @@ -588,11 +592,14 @@ function auth_aclcheck($id, $user, $groups) { } do { - $matches = preg_grep('/^'.preg_quote($path, '/').'\s+(\S+)\s+/'.$ci, $AUTH_ACL); + $matches = preg_grep('/^'.preg_quote($path, '/').'\s+(\S+)\s+/u', $AUTH_ACL); if(count($matches)) { foreach($matches as $match) { $match = preg_replace('/#.*$/', '', $match); //ignore comments $acl = preg_split('/\s+/', $match); + if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') { + $acl[1] = utf8_strtolower($acl[1]); + } if(!in_array($acl[1], $groups)) { continue; } -- cgit v1.2.3 From 10b5c32d6486ab0884deda109b1e5947f7ec7662 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 30 Nov 2012 12:35:46 +0100 Subject: fixed merge error in inc/auth.php merged the wrong change here --- inc/auth.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/auth.php b/inc/auth.php index 54d2cd50a..9c458338d 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -782,21 +782,21 @@ function register() { // create substitutions for use in notification email $substitutions = array( - 'NEWUSER' => $_POST['login'], - 'NEWNAME' => $_POST['fullname'], - 'NEWEMAIL' => $_POST['email'], + 'NEWUSER' => $login, + 'NEWNAME' => $fullname, + 'NEWEMAIL' => $email, ); if(!$conf['autopasswd']) { msg($lang['regsuccess2'], 1); - notify('', 'register', '', $_POST['login'], false, $substitutions); + notify('', 'register', '', $login, false, $substitutions); return true; } // autogenerated password? then send him the password if(auth_sendPassword($login, $pass)) { msg($lang['regsuccess'], 1); - notify('', 'register', '', $_POST['login'], false, $substitutions); + notify('', 'register', '', $login, false, $substitutions); return true; } else { msg($lang['regmailfail'], -1); -- cgit v1.2.3 From 790b77202079261b11d425e0c814608d626eea70 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 30 Nov 2012 13:09:15 +0100 Subject: moved registration notification to subscription class --- inc/auth.php | 14 +++++--------- inc/common.php | 27 ++------------------------- inc/subscription.php | 29 ++++++++++++++++------------- 3 files changed, 23 insertions(+), 47 deletions(-) (limited to 'inc') diff --git a/inc/auth.php b/inc/auth.php index 9c458338d..29a46b37e 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -780,23 +780,19 @@ function register() { return false; } - // create substitutions for use in notification email - $substitutions = array( - 'NEWUSER' => $login, - 'NEWNAME' => $fullname, - 'NEWEMAIL' => $email, - ); + // send notification about the new user + $subscription = new Subscription(); + $subscription->send_register($login, $fullname, $email); + // are we done? if(!$conf['autopasswd']) { msg($lang['regsuccess2'], 1); - notify('', 'register', '', $login, false, $substitutions); return true; } - // autogenerated password? then send him the password + // autogenerated password? then send password to user if(auth_sendPassword($login, $pass)) { msg($lang['regsuccess'], 1); - notify('', 'register', '', $login, false, $substitutions); return true; } else { msg($lang['regmailfail'], -1); diff --git a/inc/common.php b/inc/common.php index d17061a1b..20e0af389 100644 --- a/inc/common.php +++ b/inc/common.php @@ -1126,36 +1126,13 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = $to = $data['addresslist']; if(empty($to)) return false; $tpl = 'subscr_single'; - } elseif($who == 'register') { - if(empty($conf['registernotify'])) return false; - $text = rawLocale('registermail'); - $to = $conf['registernotify']; } else { return false; //just to be safe } // prepare content - if($who == 'register') { - $subject = $lang['mail_new_user'].' '.$summary; - } else { - $subscription = new Subscription(); - return $subscription->send_diff($to, $tpl, $id, $rev, $summary); - } - - - // send mail - $mail = new Mailer(); - $mail->to($to); - $mail->subject($subject); - $mail->setBody($text, $trep, $hrep); - if($who == 'subscribers') { - $mail->setHeader( - 'List-Unsubscribe', - '<'.wl($id, array('do'=> 'subscribe'), true, '&').'>', - false - ); - } - return $mail->send(); + $subscription = new Subscription(); + return $subscription->send_diff($to, $tpl, $id, $rev, $summary); } /** diff --git a/inc/subscription.php b/inc/subscription.php index 4757b216c..bfbd95244 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -429,9 +429,18 @@ class Subscription { ); } + /** + * Send a notify mail on new registration + * + * @author Andreas Gohr + * + * @param string $login login name of the new user + * @param string $fullname full name of the new user + * @param string $email email address of the new user + * @return bool true if a mail was sent + */ public function send_register($login, $fullname, $email) { global $conf; - global $ID; if(empty($conf['registernotify'])) return false; $trep = array( @@ -443,7 +452,7 @@ class Subscription { return $this->send( $conf['registernotify'], 'new_user', - $ID, + $login, 'registermail', $trep ); @@ -486,10 +495,10 @@ class Subscription { * @param string $subscriber_mail The target mail address * @param array $ids Array of ids * @param string $ns_id The id of the namespace - * @return bool + * @return bool true if a mail was sent */ protected function send_list($subscriber_mail, $ids, $ns_id) { - if(count($ids) === 0) return; + if(count($ids) === 0) return false; $tlist = ''; $hlist = '
    '; @@ -526,7 +535,7 @@ class Subscription { * @param string $subscriber_mail The target mail address * @param string $subject The lang id of the mail subject (without the * prefix “mail_”) - * @param string $id The page or namespace id + * @param string $context The context of this mail, eg. page or namespace id * @param string $template The name of the mail template * @param array $trep Predefined parameters used to parse the * template (in text format) @@ -534,17 +543,11 @@ class Subscription { * template (in HTML format), null to default to $trep * @return bool */ - protected function send($subscriber_mail, $subject, $id, $template, $trep, $hrep = null) { + protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null) { global $lang; $text = rawLocale($template); - $trep = array_merge( - $trep, array( - - ) - ); - - $subject = $lang['mail_'.$subject].' '.$id; + $subject = $lang['mail_'.$subject].' '.$context; $mail = new Mailer(); $mail->bcc($subscriber_mail); $mail->subject($subject); -- cgit v1.2.3 From 2e41a0255616465dae4c1883ce295ed4a9cd9a33 Mon Sep 17 00:00:00 2001 From: Hakan Sandell Date: Sun, 2 Dec 2012 20:32:38 +0100 Subject: Re-phrased "no license" installer text --- inc/lang/en/lang.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index a21db0564..6ea5acec0 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -311,7 +311,7 @@ $lang['i_pol1'] = 'Public Wiki (read for everyone, write and uplo $lang['i_pol2'] = 'Closed Wiki (read, write, upload for registered users only)'; $lang['i_retry'] = 'Retry'; $lang['i_license'] = 'Please choose the license you want to put your content under:'; -$lang['i_license_none'] = 'None of the above'; +$lang['i_license_none'] = 'Do not show any license information'; $lang['recent_global'] = 'You\'re currently watching the changes inside the %s namespace. You can also view the recent changes of the whole wiki.'; $lang['years'] = '%d years ago'; -- cgit v1.2.3 From 8740865b92ffc5ea809dce6ae44f48aa7caf4134 Mon Sep 17 00:00:00 2001 From: Hakan Sandell Date: Sun, 2 Dec 2012 20:33:29 +0100 Subject: Fixed missing %s in Swedish translation --- inc/lang/sv/lang.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/lang/sv/lang.php b/inc/lang/sv/lang.php index 1c3141da7..4c4e060b4 100644 --- a/inc/lang/sv/lang.php +++ b/inc/lang/sv/lang.php @@ -172,7 +172,7 @@ $lang['lastmod'] = 'Senast uppdaterad'; $lang['by'] = 'av'; $lang['deleted'] = 'raderad'; $lang['created'] = 'skapad'; -$lang['restored'] = 'tidigare version återställd'; +$lang['restored'] = 'tidigare version återställd (%s)'; $lang['external_edit'] = 'extern redigering'; $lang['summary'] = 'Redigeringskommentar'; $lang['noflash'] = 'Adobe Flash Plugin behövs för att visa detta innehåll.'; -- cgit v1.2.3 From b04a190d960a260703e65e9ed4a951cb083e4e61 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sun, 2 Dec 2012 23:58:38 +0100 Subject: XHTML renderer: bind section id counter to the renderer instance This changes the previously static lastsecid into an instance variable, this leads to consistent section edit ids when the xhtml renderer is used more than once in a request. This makes it possible for test cases to know the exact section edit id for an instruction array. --- inc/parser/xhtml.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php index b4e78a530..68b92ca43 100644 --- a/inc/parser/xhtml.php +++ b/inc/parser/xhtml.php @@ -30,6 +30,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer { var $toc = array(); // will contain the Table of Contents var $sectionedits = array(); // A stack of section edit data + private $lastsecid = 0; // last section edit id, used by startSectionEdit var $headers = array(); var $footnotes = array(); @@ -50,9 +51,8 @@ class Doku_Renderer_xhtml extends Doku_Renderer { * @author Adrian Lang */ public function startSectionEdit($start, $type, $title = null) { - static $lastsecid = 0; - $this->sectionedits[] = array(++$lastsecid, $start, $type, $title); - return 'sectionedit' . $lastsecid; + $this->sectionedits[] = array(++$this->lastsecid, $start, $type, $title); + return 'sectionedit' . $this->lastsecid; } /** -- cgit v1.2.3 From 48a1417ef1d153f5c7994aa7ced8da4e710bc70e Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Mon, 3 Dec 2012 00:04:36 +0100 Subject: Correct parameter documentation of DokuWiki_Plugin::loadHelper() --- inc/plugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/plugin.php b/inc/plugin.php index b0518346d..e4bba989d 100644 --- a/inc/plugin.php +++ b/inc/plugin.php @@ -184,8 +184,8 @@ class DokuWiki_Plugin { * * @author Esther Brunner * - * @param $name name of plugin to load - * @param $msg message to display in case the plugin is not available + * @param string $name name of plugin to load + * @param bool $msg if a message should be displayed in case the plugin is not available * * @return object helper plugin object */ -- cgit v1.2.3 From 03aafe1cc677b4b0b0b0ef9ed04d8ab72f3ce583 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Mon, 3 Dec 2012 00:13:01 +0100 Subject: Indexer: add getPID/getPageFromPID functions and PID to INDEXER_PAGE_ADD This allows plugins to get the PID for a page and also to get the page for a certain PID. That way plugins can build their own index that uses numeric ids. --- inc/indexer.php | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 6 deletions(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index f22aee3a0..cbb817d78 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -120,7 +120,7 @@ class Doku_Indexer { return "locked"; // load known documents - $pid = $this->addIndexKey('page', '', $page); + $pid = $this->getPIDNoLock($page); if ($pid === false) { $this->unlock(); return false; @@ -256,7 +256,7 @@ class Doku_Indexer { return "locked"; // load known documents - $pid = $this->addIndexKey('page', '', $page); + $pid = $this->getPIDNoLock($page); if ($pid === false) { $this->unlock(); return false; @@ -348,7 +348,7 @@ class Doku_Indexer { return "locked"; // load known documents - $pid = $this->addIndexKey('page', '', $page); + $pid = $this->getPIDNoLock($page); if ($pid === false) { $this->unlock(); return false; @@ -453,6 +453,48 @@ class Doku_Indexer { return array_values($wordlist); } + /** + * Get the numeric PID of a page + * + * @param string $page The page to get the PID for + * @return bool|int The page id on success, false on error + */ + public function getPID($page) { + if (!$this->lock()) + return false; + + // load known documents + $pid = $this->getPIDNoLock($page); + if ($pid === false) { + $this->unlock(); + return false; + } + + $this->unlock(); + return $pid; + } + + /** + * Get the numeric PID of a page without locking the index. + * Only use this function when the index is already locked. + * + * @param string $page The page to get the PID for + * @return bool|int The page id on success, false on error + */ + protected function getPIDNoLock($page) { + return $this->addIndexKey('page', '', $page); + } + + /** + * Get the page id of a numeric PID + * + * @param int $pid The PID to get the page id for + * @return string The page id + */ + public function getPageFromPID($pid) { + return $this->getIndexKey('page', '', $pid); + } + /** * Find pages in the fulltext index containing the words, * @@ -946,7 +988,7 @@ class Doku_Indexer { * @param string $idx name of the index * @param string $suffix subpart identifier * @param string $value line to find in the index - * @return int line number of the value in the index + * @return int|bool line number of the value in the index or false if writing the index failed * @author Tom N Harris */ protected function addIndexKey($idx, $suffix, $value) { @@ -1223,6 +1265,12 @@ function idx_addPage($page, $verbose=false, $force=false) { return $result; } + $Indexer = idx_get_indexer(); + $pid = $Indexer->getPID($page); + if ($pid === false) { + if ($verbose) print("Indexer: getting the PID failed for $page".DOKU_LF); + return false; + } $body = ''; $metadata = array(); $metadata['title'] = p_get_metadata($page, 'title', METADATA_RENDER_UNLIMITED); @@ -1230,14 +1278,13 @@ function idx_addPage($page, $verbose=false, $force=false) { $metadata['relation_references'] = array_keys($references); else $metadata['relation_references'] = array(); - $data = compact('page', 'body', 'metadata'); + $data = compact('page', 'body', 'metadata', 'pid'); $evt = new Doku_Event('INDEXER_PAGE_ADD', $data); if ($evt->advise_before()) $data['body'] = $data['body'] . " " . rawWiki($page); $evt->advise_after(); unset($evt); extract($data); - $Indexer = idx_get_indexer(); $result = $Indexer->addPageWords($page, $body); if ($result === "locked") { if ($verbose) print("Indexer: locked".DOKU_LF); -- cgit v1.2.3 From 3d2ce006bd7cffab5cda27f01787d2fd66ab630e Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Tue, 4 Dec 2012 16:13:46 +0100 Subject: Indexer: Add cache for getPID() This avoids re-reading of the page index file for every getPID()-call by using a simple FIFO cache, limited to 10 items. In idx_addPage() and the functions that it calls getPID() is called 3 times for the same PID. --- inc/indexer.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index cbb817d78..05b082872 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -102,6 +102,10 @@ function wordlen($w){ * @author Tom N Harris */ class Doku_Indexer { + /** + * @var array $pidCache Cache for getPID() + */ + protected $pidCache = array(); /** * Adds the contents of a page to the fulltext index @@ -460,6 +464,9 @@ class Doku_Indexer { * @return bool|int The page id on success, false on error */ public function getPID($page) { + // return PID without locking when it is in the cache + if (isset($this->pidCache[$page])) return $this->pidCache[$page]; + if (!$this->lock()) return false; @@ -482,7 +489,14 @@ class Doku_Indexer { * @return bool|int The page id on success, false on error */ protected function getPIDNoLock($page) { - return $this->addIndexKey('page', '', $page); + // avoid expensive addIndexKey operation for the most recently requested pages by using a cache + if (isset($this->pidCache[$page])) return $this->pidCache[$page]; + $pid = $this->addIndexKey('page', '', $page); + // limit cache to 10 entries by discarding the oldest element as in DokuWiki usually only the most recently + // added item will be requested again + if (count($this->pidCache) > 10) array_shift($this->pidCache); + $this->pidCache[$page] = $pid; + return $pid; } /** -- cgit v1.2.3 From 1421e5483ad1ca8780331077141e0c07b6530bc5 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Tue, 4 Dec 2012 16:15:49 +0100 Subject: Indexer: Make $Indexer in idx_get_indexer() global instead of static This avoids problems with test cases that use the indexer and the PID cache - the index is cleaned between test cases but the PID cache wasn't cleaned. Now PHPUnit can unset the global $Indexer between test cases. --- inc/indexer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index 05b082872..4dfaa33fb 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -1196,8 +1196,8 @@ class Doku_Indexer { * @author Tom N Harris */ function idx_get_indexer() { - static $Indexer = null; - if (is_null($Indexer)) { + global $Indexer; + if (!isset($Indexer)) { $Indexer = new Doku_Indexer(); } return $Indexer; -- cgit v1.2.3 From e13e01da48b642dcdf3efe6864f0fc2bb5094455 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 7 Dec 2012 10:35:27 +0100 Subject: added compatibility function --- inc/subscription.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index bfbd95244..df6f1af32 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -604,4 +604,19 @@ class Subscription { } $data['addresslist'] = trim($addresslist.','.implode(',', $result), ','); } +} + +/** + * Compatibility wrapper around Subscription:notifyaddresses + * + * for plugins emitting COMMON_NOTIFY_ADDRESSLIST themselves and relying on on this to + * be the default handler + * + * @param array $data event data for + * + * @deprecated 2012-12-07 + */ +function subscription_addresslist(&$data){ + $sub = new Subscription(); + $sub->notifyaddresses($data); } \ No newline at end of file -- cgit v1.2.3 From b34cf753be6c2c3fc67eac58738acc74a1c1785a Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 7 Dec 2012 10:35:49 +0100 Subject: some reformatting --- inc/subscription.php | 78 ++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index df6f1af32..44248a39d 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -93,27 +93,27 @@ class Subscription { */ protected function buildregex($user = null, $style = null, $data = null) { // always work with arrays - $user = (array) $user; + $user = (array) $user; $style = (array) $style; - $data = (array) $data; + $data = (array) $data; // clean - $user = array_filter(array_map('trim', $user)); + $user = array_filter(array_map('trim', $user)); $style = array_filter(array_map('trim', $style)); - $data = array_filter(array_map('trim', $data)); + $data = array_filter(array_map('trim', $data)); // user names are encoded $user = array_map('auth_nameencode', $user); // quote - $user = array_map('preg_quote_cb', $user); + $user = array_map('preg_quote_cb', $user); $style = array_map('preg_quote_cb', $style); - $data = array_map('preg_quote_cb', $data); + $data = array_map('preg_quote_cb', $data); // join - $user = join('|', $user); + $user = join('|', $user); $style = join('|', $style); - $data = join('|', $data); + $data = join('|', $data); // any data at all? if($user.$style.$data === '') throw new Exception('no data passed'); @@ -126,7 +126,7 @@ class Subscription { } if($style === '') { $style = '\S+'; - $sopt = '?'; + $sopt = '?'; } if($data === '') { $data = '\S+'; @@ -158,7 +158,7 @@ class Subscription { $files = array(':' => $this->file(':')); do { $files[$page] = $this->file($page); - $page = getNS(rtrim($page, ':')).':'; + $page = getNS(rtrim($page, ':')).':'; } while($page !== ':'); $re = $this->buildregex($user, $style, $data); @@ -204,9 +204,9 @@ class Subscription { // delete any existing subscription $this->remove($id, $user); - $user = auth_nameencode(trim($user)); + $user = auth_nameencode(trim($user)); $style = trim($style); - $data = trim($data); + $data = trim($data); if(!$user) throw new Exception('no subscription user given'); if(!$style) throw new Exception('no subscription style given'); @@ -270,8 +270,8 @@ class Subscription { foreach($subs as $target => $info) { $result[] = array( 'target' => $target, - 'style' => $info[$user][0], - 'data' => $info[$user][1] + 'style' => $info[$user][0], + 'data' => $info[$user][1] ); } @@ -302,7 +302,7 @@ class Subscription { // remember current user info $olduinfo = $USERINFO; - $olduser = $_SERVER['REMOTE_USER']; + $olduser = $_SERVER['REMOTE_USER']; foreach($subscriptions as $target => $users) { if(!$this->lock($target)) continue; @@ -318,7 +318,7 @@ class Subscription { } // Work as the user to make sure ACLs apply correctly - $USERINFO = $auth->getUserData($user); + $USERINFO = $auth->getUserData($user); $_SERVER['REMOTE_USER'] = $user; if($USERINFO === false) continue; if(!$USERINFO['mail']) continue; @@ -329,7 +329,7 @@ class Subscription { } else { // single page subscription, check ACL ourselves if(auth_quickaclcheck($target) < AUTH_READ) continue; - $meta = p_get_metadata($target); + $meta = p_get_metadata($target); $changes = array($meta['last_change']); } @@ -372,7 +372,7 @@ class Subscription { } // restore current user info - $USERINFO = $olduinfo; + $USERINFO = $olduinfo; $_SERVER['REMOTE_USER'] = $olduser; return $count; } @@ -392,32 +392,32 @@ class Subscription { // prepare replacements (keys not set in hrep will be taken from trep) $trep = array( - 'PAGE' => $id, - 'NEWPAGE' => wl($id, '', true, '&'), - 'SUMMARY' => $summary, + 'PAGE' => $id, + 'NEWPAGE' => wl($id, '', true, '&'), + 'SUMMARY' => $summary, 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&') ); $hrep = array(); if($rev) { - $subject = 'changed'; + $subject = 'changed'; $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); - $df = new Diff(explode("\n", rawWiki($id, $rev)), - explode("\n", rawWiki($id))); - $dformat = new UnifiedDiffFormatter(); - $tdiff = $dformat->format($df); + $df = new Diff(explode("\n", rawWiki($id, $rev)), + explode("\n", rawWiki($id))); + $dformat = new UnifiedDiffFormatter(); + $tdiff = $dformat->format($df); $DIFF_INLINESTYLES = true; - $dformat = new InlineDiffFormatter(); - $hdiff = $dformat->format($df); - $hdiff = ''.$hdiff.'
    '; + $dformat = new InlineDiffFormatter(); + $hdiff = $dformat->format($df); + $hdiff = ''.$hdiff.'
    '; $DIFF_INLINESTYLES = false; } else { - $subject = 'newpage'; + $subject = 'newpage'; $trep['OLDPAGE'] = '---'; - $tdiff = rawWiki($id); - $hdiff = nl2br(hsc($tdiff)); + $tdiff = rawWiki($id); + $hdiff = nl2br(hsc($tdiff)); } $trep['DIFF'] = $tdiff; @@ -444,8 +444,8 @@ class Subscription { if(empty($conf['registernotify'])) return false; $trep = array( - 'NEWUSER' => $login, - 'NEWNAME' => $fullname, + 'NEWUSER' => $login, + 'NEWNAME' => $fullname, 'NEWEMAIL' => $email, ); @@ -548,11 +548,11 @@ class Subscription { $text = rawLocale($template); $subject = $lang['mail_'.$subject].' '.$context; - $mail = new Mailer(); + $mail = new Mailer(); $mail->bcc($subscriber_mail); $mail->subject($subject); $mail->setBody($text, $trep, $hrep); - if(isset($trep['SUBSCRIBE'])){ + if(isset($trep['SUBSCRIBE'])) { $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); } return $mail->send(); @@ -580,8 +580,8 @@ class Subscription { global $auth; global $conf; - $id = $data['id']; - $self = $data['self']; + $id = $data['id']; + $self = $data['self']; $addresslist = $data['addresslist']; $subscriptions = $this->subscribers($id, null, 'every'); @@ -616,7 +616,7 @@ class Subscription { * * @deprecated 2012-12-07 */ -function subscription_addresslist(&$data){ +function subscription_addresslist(&$data) { $sub = new Subscription(); $sub->notifyaddresses($data); } \ No newline at end of file -- cgit v1.2.3 From 3a0852d932a09ba01dcbcf2d371febf451f9f00c Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 7 Dec 2012 17:46:16 +0100 Subject: added option to enable automatic popularity data in installer --- inc/lang/en/lang.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'inc') diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 6ea5acec0..144faf4e1 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -312,6 +312,8 @@ $lang['i_pol2'] = 'Closed Wiki (read, write, upload for registere $lang['i_retry'] = 'Retry'; $lang['i_license'] = 'Please choose the license you want to put your content under:'; $lang['i_license_none'] = 'Do not show any license information'; +$lang['i_pop_field'] = 'Please, help us to improve the DokuWiki experience:'; +$lang['i_pop_label'] = 'Once a month, send anonymous usage data to the DokuWiki developers'; $lang['recent_global'] = 'You\'re currently watching the changes inside the %s namespace. You can also view the recent changes of the whole wiki.'; $lang['years'] = '%d years ago'; -- cgit v1.2.3 From 740da4c2e05743b1f0823b3be40c44f0fc4810a9 Mon Sep 17 00:00:00 2001 From: lupo49 Date: Wed, 12 Dec 2012 11:28:42 +0100 Subject: de/de-informal: language updates / added "sidebar" --- inc/lang/de-informal/install.html | 8 ++++---- inc/lang/de-informal/lang.php | 37 +++++++++++++++++++------------------ inc/lang/de/install.html | 8 ++++---- inc/lang/de/lang.php | 37 +++++++++++++++++++------------------ 4 files changed, 46 insertions(+), 44 deletions(-) (limited to 'inc') diff --git a/inc/lang/de-informal/install.html b/inc/lang/de-informal/install.html index 55239e6db..f0473772c 100644 --- a/inc/lang/de-informal/install.html +++ b/inc/lang/de-informal/install.html @@ -1,10 +1,10 @@ -

    Diese Seite hilft dir bei der Erst-Installation und Konfiguration von -Dokuwiki. Zusätzliche Informationen zu +

    Diese Seite hilft dir bei der Erstinstallation und Konfiguration von +DokuWiki. Zusätzliche Informationen zu diesem Installationsskript findest du auf der entsprechenden Hilfe-Seite (en).

    DokuWiki verwendet normale Dateien für das Speichern von Wikiseiten und -anderen Informationen (Bilder, Suchindizes, alte Versionen, usw.). +anderen Informationen (Bilder, Suchindizes, alte Versionen usw.). Um DokuWiki betreiben zu können, muss Schreibzugriff auf die Verzeichnisse bestehen, in denen DokuWiki diese Dateien ablegt. Dieses Installationsprogramm kann diese Rechte nicht für dich setzen. Du musst dies @@ -14,7 +14,7 @@ hostest, über FTP oder ein entsprechendes Werkzeug (z.B. cPanel) durchführen.<

    Dieses Skript hilft dir beim ersten Einrichten des Zugangsschutzes (ACL) von DokuWiki, welcher eine Administratoranmeldung und damit Zugang zum Administrationsmenü ermöglicht. -Dort kannst du dann weitere Tätigkeiten wie das Installieren von Plugins, das +Dort kannst du dann weitere Tätigkeiten wie das Installieren von Plugins, dass Verwalten von Nutzern und das Ändern von Konfigurationseinstellungen durchführen. Das Nutzen der Zugangskontrolle ist nicht zwingend erforderlich, es erleichtert aber die Administration von DokuWiki.

    diff --git a/inc/lang/de-informal/lang.php b/inc/lang/de-informal/lang.php index 3b6a28040..3b19f749d 100644 --- a/inc/lang/de-informal/lang.php +++ b/inc/lang/de-informal/lang.php @@ -1,6 +1,6 @@ @@ -67,7 +67,7 @@ $lang['user'] = 'Benutzername'; $lang['pass'] = 'Passwort'; $lang['newpass'] = 'Neues Passwort'; $lang['oldpass'] = 'Bestätigen (Altes Passwort)'; -$lang['passchk'] = 'und nochmal'; +$lang['passchk'] = 'Passwort erneut eingeben'; $lang['remember'] = 'Angemeldet bleiben'; $lang['fullname'] = 'Voller Name'; $lang['email'] = 'E-Mail'; @@ -83,7 +83,7 @@ $lang['regsuccess2'] = 'Der neue Nutzer wurde angelegt.'; $lang['regmailfail'] = 'Offenbar ist ein Fehler beim Versenden der Passwortmail aufgetreten. Bitte wende dich an den Wiki-Admin.'; $lang['regbadmail'] = 'Die angegebene Mail-Adresse scheint ungültig zu sein. Falls dies ein Fehler ist, wende dich bitte an den Wiki-Admin.'; $lang['regbadpass'] = 'Die beiden eingegeben Passwörter stimmen nicht überein. Bitte versuche es noch einmal.'; -$lang['regpwmail'] = 'Ihr DokuWiki Passwort'; +$lang['regpwmail'] = 'Ihr DokuWiki-Passwort'; $lang['reghere'] = 'Du hast noch keinen Zugang? Hier registrieren'; $lang['profna'] = 'Änderung des Benutzerprofils in diesem Wiki nicht möglich.'; $lang['profnochange'] = 'Keine Änderungen, nichts zu tun.'; @@ -92,9 +92,9 @@ $lang['profchanged'] = 'Benutzerprofil erfolgreich geändert.'; $lang['pwdforget'] = 'Passwort vergessen? Fordere ein neues an'; $lang['resendna'] = 'Passwörter versenden ist in diesem Wiki nicht möglich.'; $lang['resendpwd'] = 'Neues Passwort setzen für'; -$lang['resendpwdmissing'] = 'Es tut mir Leid, aber du musst alle Felder ausfüllen.'; -$lang['resendpwdnouser'] = 'Es tut mir Leid, aber der Benutzer existiert nicht in unserer Datenbank.'; -$lang['resendpwdbadauth'] = 'Es tut mir Leid, aber dieser Authentifizierungscode ist ungültig. Stelle sicher, dass du den kompletten Bestätigungslink verwendet haben.'; +$lang['resendpwdmissing'] = 'Es tut mir leid, aber du musst alle Felder ausfüllen.'; +$lang['resendpwdnouser'] = 'Es tut mir leid, aber der Benutzer existiert nicht in unserer Datenbank.'; +$lang['resendpwdbadauth'] = 'Es tut mir leid, aber dieser Authentifizierungscode ist ungültig. Stelle sicher, dass du den kompletten Bestätigungslink verwendet haben.'; $lang['resendpwdconfirm'] = 'Ein Bestätigungslink wurde per E-Mail versandt.'; $lang['resendpwdsuccess'] = 'Dein neues Passwort wurde per E-Mail versandt.'; $lang['license'] = 'Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht:'; @@ -137,7 +137,7 @@ $lang['js']['nosmblinks'] = 'Das Verlinken von Windows-Freigaben funktionie $lang['js']['linkwiz'] = 'Link-Assistent'; $lang['js']['linkto'] = 'Link zu:'; $lang['js']['del_confirm'] = 'Die ausgewählten Dateien wirklich löschen?'; -$lang['js']['restore_confirm'] = 'Wirkliich diese Version wieder herstellen?'; +$lang['js']['restore_confirm'] = 'Wirklich diese Version wiederherstellen?'; $lang['js']['media_diff'] = 'Unterschiede anzeigen:'; $lang['js']['media_diff_both'] = 'Seite für Seite'; $lang['js']['media_diff_opacity'] = 'Überblenden'; @@ -161,7 +161,7 @@ $lang['uploadspam'] = 'Hochladen verweigert: Treffer auf der Spamlist $lang['uploadxss'] = 'Hochladen verweigert: Daten scheinen Schadcode zu enthalten.'; $lang['uploadsize'] = 'Die hochgeladene Datei war zu groß. (max. %s)'; $lang['deletesucc'] = 'Die Datei "%s" wurde gelöscht.'; -$lang['deletefail'] = '"%s" konnte nicht gelöscht werden. Keine Berechtigung?.'; +$lang['deletefail'] = '"%s" konnte nicht gelöscht werden. Keine Berechtigung?.'; $lang['mediainuse'] = 'Die Datei "%s" wurde nicht gelöscht. Sie wird noch verwendet.'; $lang['namespaces'] = 'Namensräume'; $lang['mediafiles'] = 'Vorhandene Dateien in'; @@ -181,7 +181,7 @@ $lang['current'] = 'aktuell'; $lang['yours'] = 'Deine Version'; $lang['diff'] = 'Zeige Unterschiede zu aktueller Version'; $lang['diff2'] = 'Zeige Unterschiede der ausgewählten Versionen'; -$lang['difflink'] = 'Link zu der Versionshistorie'; +$lang['difflink'] = 'Link zu der Vergleichsansicht'; $lang['diff_type'] = 'Unterschiede anzeigen:'; $lang['diff_inline'] = 'Inline'; $lang['diff_side'] = 'Side by Side'; @@ -196,15 +196,16 @@ $lang['restored'] = 'alte Version wiederhergestellt (%s)'; $lang['external_edit'] = 'Externe Bearbeitung'; $lang['summary'] = 'Zusammenfassung'; $lang['noflash'] = 'Das Adobe Flash Plugin wird benötigt, um diesen Inhalt anzuzeigen.'; -$lang['download'] = 'Download-Teil'; +$lang['download'] = 'Schnipsel herunterladen'; $lang['tools'] = 'Werkzeuge'; $lang['user_tools'] = 'Benutzer-Werkzeuge'; $lang['site_tools'] = 'Webseiten-Werkzeuge'; $lang['page_tools'] = 'Seiten-Werkzeuge'; $lang['skip_to_content'] = 'zum Inhalt springen'; +$lang['sidebar'] = 'Seitenleiste'; $lang['mail_newpage'] = 'Neue Seite:'; $lang['mail_changed'] = 'Seite geändert:'; -$lang['mail_subscribe_list'] = 'Seite hat sich im Namespace geändert:'; +$lang['mail_subscribe_list'] = 'Geänderte Seiten im Namensraum:'; $lang['mail_new_user'] = 'Neuer Benutzer:'; $lang['mail_upload'] = 'Datei hochgeladen:'; $lang['changes_type'] = 'Änderungen anzeigen von'; @@ -272,14 +273,14 @@ $lang['subscr_style_digest'] = 'E-Mail mit zusammengefasster Übersicht der Se $lang['subscr_style_list'] = 'Auflistung aller geänderten Seiten seit der letzten E-Mail (alle %.2f Tage)'; $lang['authmodfailed'] = 'Benutzerüberprüfung nicht möglich. Bitte wende dich an den Admin.'; $lang['authtempfail'] = 'Benutzerüberprüfung momentan nicht möglich. Falls das Problem andauert, wende dich an den Admin.'; -$lang['authpwdexpire'] = 'Dein Passwort läuft in %d Tag(en) ab, du solltest es es bald ändern.'; +$lang['authpwdexpire'] = 'Dein Passwort läuft in %d Tag(en) ab. Du solltest es es frühzeitig ändern.'; $lang['i_chooselang'] = 'Wähle deine Sprache'; $lang['i_installer'] = 'DokuWiki-Installation'; $lang['i_wikiname'] = 'Wiki-Name'; $lang['i_enableacl'] = 'Zugangskontrolle (ACL) aktivieren (empfohlen)'; $lang['i_superuser'] = 'Benutzername des Administrators'; $lang['i_problems'] = 'Das Installationsprogramm hat unten aufgeführte Probleme festgestellt, die zunächst behoben werden müssen, bevor du mit der Installation fortfahren kannst.'; -$lang['i_modified'] = 'Aus Sicherheitsgründen arbeitet dieses Script nur mit einer neuen, unmodifizierten DokuWiki-Installation. Du solltest entweder alle Dateien noch einmal frisch installieren oder die Dokuwiki-Installationsanleitung konsultieren.'; +$lang['i_modified'] = 'Aus Sicherheitsgründen arbeitet dieses Skript nur mit einer neuen bzw. nicht modifizierten DokuWiki-Installation. Du solltest entweder alle Dateien noch einmal frisch installieren oder die Dokuwiki-Installationsanleitung konsultieren.'; $lang['i_funcna'] = 'Die PHP-Funktion %s ist nicht verfügbar. Unter Umständen wurde sie von deinem Hoster deaktiviert?'; $lang['i_phpver'] = 'Deine PHP-Version %s ist niedriger als die benötigte Version %s. Bitte aktualisiere deine PHP-Installation.'; $lang['i_permfail'] = '%s ist nicht durch DokuWiki beschreibbar. Du musst die Berechtigungen dieses Ordners ändern!'; @@ -288,11 +289,11 @@ $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. D $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; $lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Du kannst jetzt die install.php löschen. Dein neues DokuWiki ist jetzt für dich bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese vermutlich von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; -$lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; -$lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; -$lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; -$lang['i_pol2'] = 'Geschlossenes Wiki (lesen, schreiben, hochladen nur für registrierte Nutzer)'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Du musst diese von Hand beheben, bevor du dein neues DokuWiki nutzen kannst.'; +$lang['i_policy'] = 'Anfangseinstellungen der Zugangskontrolle (ACL)'; +$lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben und hochladen für alle Nutzer)'; +$lang['i_pol1'] = 'Öffentliches Wiki (Lesen für alle, Schreiben und Hochladen nur für registrierte Nutzer)'; +$lang['i_pol2'] = 'Geschlossenes Wiki (Lesen, Schreiben und Hochladen nur für registrierte Nutzer)'; $lang['i_retry'] = 'Wiederholen'; $lang['i_license'] = 'Bitte wähle die Lizenz aus unter der die Wiki-Inhalte veröffentlicht werden sollen:'; $lang['recent_global'] = 'Im Moment siehst du die Änderungen im Namensraum %s. Du kannst auch die Änderungen im gesamten Wiki sehen.'; diff --git a/inc/lang/de/install.html b/inc/lang/de/install.html index 599eb983b..5af3198d4 100644 --- a/inc/lang/de/install.html +++ b/inc/lang/de/install.html @@ -1,10 +1,10 @@ -

    Diese Seite hilft Ihnen bei der Erst-Installation und Konfiguration von -Dokuwiki. Zusätzliche Informationen zu +

    Diese Seite hilft Ihnen bei der Erstinstallation und Konfiguration von +DokuWiki. Zusätzliche Informationen zu diesem Installationsskript finden Sie auf der entsprechenden Hilfe Seite (en).

    DokuWiki verwendet normale Dateien für das Speichern von Wikiseiten und -anderen Informationen (Bilder, Suchindizes, alte Versionen, usw.). +anderen Informationen (Bilder, Suchindizes, alte Versionen usw.). Um DokuWiki betreiben zu können, muss Schreibzugriff auf die Verzeichnisse bestehen, in denen DokuWiki diese Dateien ablegt. Dieses Installationsprogramm kann diese Rechte nicht für Sie setzen. Sie müssen dies @@ -14,7 +14,7 @@ hosten, über FTP oder ein entsprechendes Werkzeug (z.B. cPanel) durchführen.Dieses Skript hilft Ihnen beim ersten Einrichten des Zugangsschutzes (ACL) von DokuWiki, welcher eine Administratoranmeldung und damit Zugang zum Administrationsmenu ermöglicht. -Dort können Sie dann weitere Tätigkeiten wie das Installieren von Plugins, das +Dort können Sie dann weitere Tätigkeiten wie das Installieren von Plugins, dass Verwalten von Nutzern und das Ändern von Konfigurationseinstellungen durchführen. Das Nutzen der Zugangskontrolle ist nicht zwingend erforderlich, es erleichtert aber die Administration von DokuWiki.

    diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php index 23c6a0dc1..160ea53e8 100644 --- a/inc/lang/de/lang.php +++ b/inc/lang/de/lang.php @@ -69,7 +69,7 @@ $lang['user'] = 'Benutzername'; $lang['pass'] = 'Passwort'; $lang['newpass'] = 'Neues Passwort'; $lang['oldpass'] = 'Bestätigen (Altes Passwort)'; -$lang['passchk'] = 'und nochmal'; +$lang['passchk'] = 'Passwort erneut eingeben'; $lang['remember'] = 'Angemeldet bleiben'; $lang['fullname'] = 'Voller Name'; $lang['email'] = 'E-Mail'; @@ -85,7 +85,7 @@ $lang['regsuccess2'] = 'Der neue Nutzer wurde angelegt.'; $lang['regmailfail'] = 'Offenbar ist ein Fehler beim Versenden der Passwort-E-Mail aufgetreten. Bitte wenden Sie sich an den Wiki-Admin.'; $lang['regbadmail'] = 'Die angegebene E-Mail-Adresse scheint ungültig zu sein. Falls dies ein Fehler ist, wenden Sie sich bitte an den Wiki-Admin.'; $lang['regbadpass'] = 'Die beiden eingegeben Passwörter stimmen nicht überein. Bitte versuchen Sie es noch einmal.'; -$lang['regpwmail'] = 'Ihr DokuWiki Passwort'; +$lang['regpwmail'] = 'Ihr DokuWiki-Passwort'; $lang['reghere'] = 'Sie haben noch keinen Zugang? Hier registrieren'; $lang['profna'] = 'Änderung des Benutzerprofils in diesem Wiki nicht möglich.'; $lang['profnochange'] = 'Keine Änderungen, nichts zu tun.'; @@ -94,9 +94,9 @@ $lang['profchanged'] = 'Benutzerprofil erfolgreich geändert.'; $lang['pwdforget'] = 'Passwort vergessen? Fordere ein neues an'; $lang['resendna'] = 'Passwörter versenden ist in diesem Wiki nicht möglich.'; $lang['resendpwd'] = 'Neues Passwort setzen für'; -$lang['resendpwdmissing'] = 'Es tut mir Leid, aber Sie müssen alle Felder ausfüllen.'; -$lang['resendpwdnouser'] = 'Es tut mir Leid, aber der Benutzer existiert nicht in unserer Datenbank.'; -$lang['resendpwdbadauth'] = 'Es tut mir Leid, aber dieser Authentifizierungscode ist ungültig. Stellen Sie sicher, dass Sie den kompletten Bestätigungslink verwendet haben.'; +$lang['resendpwdmissing'] = 'Es tut mir leid, aber Sie müssen alle Felder ausfüllen.'; +$lang['resendpwdnouser'] = 'Es tut mir leid, aber der Benutzer existiert nicht in unserer Datenbank.'; +$lang['resendpwdbadauth'] = 'Es tut mir leid, aber dieser Authentifizierungscode ist ungültig. Stellen Sie sicher, dass Sie den kompletten Bestätigungslink verwendet haben.'; $lang['resendpwdconfirm'] = 'Ein Bestätigungslink wurde per E-Mail versandt.'; $lang['resendpwdsuccess'] = 'Ihr neues Passwort wurde per E-Mail versandt.'; $lang['license'] = 'Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht:'; @@ -139,7 +139,7 @@ $lang['js']['nosmblinks'] = 'Das Verlinken von Windows-Freigaben funktionie $lang['js']['linkwiz'] = 'Link-Assistent'; $lang['js']['linkto'] = 'Link nach:'; $lang['js']['del_confirm'] = 'Eintrag wirklich löschen?'; -$lang['js']['restore_confirm'] = 'Really restore this version?'; +$lang['js']['restore_confirm'] = 'Wirklich diese Version wiederherstellen?'; $lang['js']['media_diff'] = 'Unterschiede anzeigen:'; $lang['js']['media_diff_both'] = 'Side by Side'; $lang['js']['media_diff_opacity'] = 'Überblenden'; @@ -204,6 +204,7 @@ $lang['user_tools'] = 'Benutzer-Werkzeuge'; $lang['site_tools'] = 'Webseiten-Werkzeuge'; $lang['page_tools'] = 'Seiten-Werkzeuge'; $lang['skip_to_content'] = 'zum Inhalt springen'; +$lang['sidebar'] = 'Seitenleiste'; $lang['mail_newpage'] = 'Neue Seite:'; $lang['mail_changed'] = 'Seite geändert:'; $lang['mail_subscribe_list'] = 'Geänderte Seiten im Namensraum:'; @@ -272,16 +273,16 @@ $lang['subscr_m_receive'] = 'Benachrichtigung'; $lang['subscr_style_every'] = 'E-Mail bei jeder Bearbeitung'; $lang['subscr_style_digest'] = 'Zusammenfassung der Änderungen für jede veränderte Seite (Alle %.2f Tage)'; $lang['subscr_style_list'] = 'Liste der geänderten Seiten (Alle %.2f Tage)'; -$lang['authmodfailed'] = 'Benutzerüberprüfung nicht möglich. Bitte wenden Sie sich an den Systembetreuer.'; -$lang['authtempfail'] = 'Benutzerüberprüfung momentan nicht möglich. Falls das Problem andauert, wenden Sie sich an den Systembetreuer.'; -$lang['authpwdexpire'] = 'Ihr Passwort läuft in %d Tag(en) ab, Sie sollten es bald ändern.'; +$lang['authmodfailed'] = 'Benutzerüberprüfung nicht möglich. Bitte wenden Sie sich an den Admin.'; +$lang['authtempfail'] = 'Benutzerüberprüfung momentan nicht möglich. Falls das Problem andauert, wenden Sie sich an den Admin.'; +$lang['authpwdexpire'] = 'Ihr Passwort läuft in %d Tag(en) ab. Sie sollten es frühzeitig ändern.'; $lang['i_chooselang'] = 'Wählen Sie Ihre Sprache'; $lang['i_installer'] = 'DokuWiki Installation'; $lang['i_wikiname'] = 'Wiki-Name'; $lang['i_enableacl'] = 'Zugangskontrolle (ACL) aktivieren (empfohlen)'; -$lang['i_superuser'] = 'Administrator Benutzername'; +$lang['i_superuser'] = 'Benutzername des Administrators'; $lang['i_problems'] = 'Das Installationsprogramm hat unten aufgeführte Probleme festgestellt, die zunächst behoben werden müssen bevor Sie mit der Installation fortfahren können.'; -$lang['i_modified'] = 'Aus Sicherheitsgründen arbeitet dieses Script nur mit einer neuen, unmodifizierten DokuWiki Installation. Sie sollten entweder alle Dateien noch einmal frisch installieren oder die Dokuwiki-Installationsanleitung konsultieren.'; +$lang['i_modified'] = 'Aus Sicherheitsgründen arbeitet dieses Skript nur mit einer neuen bzw. nicht modifizierten DokuWiki Installation. Sie sollten entweder alle Dateien noch einmal frisch installieren oder die Dokuwiki-Installationsanleitung konsultieren.'; $lang['i_funcna'] = 'Die PHP-Funktion %s ist nicht verfügbar. Unter Umständen wurde sie von Ihrem Hoster deaktiviert?'; $lang['i_phpver'] = 'Ihre PHP-Version %s ist niedriger als die benötigte Version %s. Bitte aktualisieren Sie Ihre PHP-Installation.'; $lang['i_permfail'] = '%s ist nicht durch DokuWiki beschreibbar. Sie müssen die Berechtigungen dieses Ordners ändern!'; @@ -290,11 +291,11 @@ $lang['i_writeerr'] = '%s konnte nicht erzeugt werden. S $lang['i_badhash'] = 'Unbekannte oder modifizierte dokuwiki.php (Hash=%s)'; $lang['i_badval'] = '%s - unerlaubter oder leerer Wert'; $lang['i_success'] = 'Die Konfiguration wurde erfolgreich abgeschlossen. Sie können jetzt die install.php löschen. Ihr neues DokuWiki ist jetzt für Sie bereit.'; -$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese vermutlich von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; -$lang['i_policy'] = 'Anfangseinstellung für Zugangskontrolle (ACL)'; -$lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben, hochladen für alle)'; -$lang['i_pol1'] = 'Öffentliches Wiki (lesen für alle, schreiben und hochladen für registrierte Nutzer)'; -$lang['i_pol2'] = 'Geschlossenes Wiki (lesen, schreiben, hochladen nur für registrierte Nutzer)'; +$lang['i_failure'] = 'Es sind Fehler beim Schreiben der Konfigurationsdateien aufgetreten. Sie müssen diese von Hand beheben, bevor Sie Ihr neues DokuWiki nutzen können.'; +$lang['i_policy'] = 'Anfangseinstellungen der Zugangskontrolle (ACL)'; +$lang['i_pol0'] = 'Offenes Wiki (lesen, schreiben und hochladen für alle Nutzer)'; +$lang['i_pol1'] = 'Öffentliches Wiki (Lesen für alle, Schreiben und Hochladen nur für registrierte Nutzer)'; +$lang['i_pol2'] = 'Geschlossenes Wiki (Lesen, Schreiben und Hochladen nur für registrierte Nutzer)'; $lang['i_retry'] = 'Wiederholen'; $lang['i_license'] = 'Bitte wählen Sie die Lizenz, unter die Sie Ihre Inhalte stellen möchten:'; $lang['recent_global'] = 'Im Moment sehen Sie die Änderungen im Namensraum %s. Sie können auch die Änderungen im gesamten Wiki sehen.'; @@ -318,8 +319,8 @@ $lang['media_sort_name'] = 'nach Name'; $lang['media_sort_date'] = 'nach Datum'; $lang['media_namespaces'] = 'Namensraum wählen'; $lang['media_files'] = 'Dateien in %s'; -$lang['media_upload'] = 'In den %s Namespace hochladen.'; -$lang['media_search'] = 'Im Namespace %s suchen.'; +$lang['media_upload'] = 'In den %s Namensraum hochladen.'; +$lang['media_search'] = 'Im Namensraum %s suchen.'; $lang['media_view'] = '%s'; $lang['media_viewold'] = '%s in %s'; $lang['media_edit'] = '%s bearbeiten'; -- cgit v1.2.3 From 6a34de2d02e41ec96359914a9b7fe0d1867bfef9 Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Wed, 19 Dec 2012 14:02:32 +0000 Subject: added to version info if version is unknown --- inc/infoutils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/infoutils.php b/inc/infoutils.php index a9c33acfd..92607e4fa 100644 --- a/inc/infoutils.php +++ b/inc/infoutils.php @@ -77,7 +77,8 @@ function getVersionData(){ if($date) $version['date'] = $date; } }else{ - $version['date'] = 'unknown'; + global $updateVersion; + $version['date'] = 'update version '.$updateVersion; $version['type'] = 'snapshot?'; } return $version; -- cgit v1.2.3 From 27beeed6dddad3cd88bba7ba5ddba653ec86e0ff Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Wed, 19 Dec 2012 14:03:42 +0000 Subject: fixed broken @author link in farm code --- inc/farm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/farm.php b/inc/farm.php index af1035707..54fff3ec6 100644 --- a/inc/farm.php +++ b/inc/farm.php @@ -18,7 +18,7 @@ * @author Michael Klier * @author Christopher Smith * @author virtual host part of farm_confpath() based on conf_path() from Drupal.org's /includes/bootstrap.inc - * (see http://cvs.drupal.org/viewvc/drupal/drupal/includes/bootstrap.inc?view=markup) + * (see https://github.com/drupal/drupal/blob/7.x/includes/bootstrap.inc#L537) * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) */ -- cgit v1.2.3 From 3cf3c7d60c81d562c8331a377a76e86d41f8f528 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Thu, 20 Dec 2012 17:47:05 +0100 Subject: Add clear function to the indexer that deletes the whole index --- inc/indexer.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index 4dfaa33fb..b9eaf31d9 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -401,6 +401,38 @@ class Doku_Indexer { return true; } + /** + * Clear the whole index + * + * @return bool If the index has been cleared successfully + */ + public function clear() { + global $conf; + + if (!$this->lock()) return false; + + @unlink($conf['indexdir'].'/page.idx'); + @unlink($conf['indexdir'].'/title.idx'); + @unlink($conf['indexdir'].'/pageword.idx'); + @unlink($conf['indexdir'].'/metadata.idx'); + $dir = @opendir($conf['indexdir']); + if($dir!==false){ + while(($f = readdir($dir)) !== false){ + if(substr($f,-4)=='.idx' && + (substr($f,0,1)=='i' || substr($f,0,1)=='w' + || substr($f,-6)=='_w.idx' || substr($f,-6)=='_i.idx' || substr($f,-6)=='_p.idx')) + @unlink($conf['indexdir']."/$f"); + } + } + @unlink($conf['indexdir'].'/lengths.idx'); + + // clear the pid cache + $this->pidCache = array(); + + $this->unlock(); + return true; + } + /** * Split the text into words for fulltext search * -- cgit v1.2.3 From 4f7083212993bf72f0b8969e6d57f587faaa5dfc Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Thu, 20 Dec 2012 17:47:39 +0100 Subject: Remove global $Indexer, clear index in the testing system instead --- inc/indexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index b9eaf31d9..7a62345bf 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -1228,7 +1228,7 @@ class Doku_Indexer { * @author Tom N Harris */ function idx_get_indexer() { - global $Indexer; + static $Indexer; if (!isset($Indexer)) { $Indexer = new Doku_Indexer(); } -- cgit v1.2.3 From def492a2d77af9effca204483b570061202cda5b Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Sun, 6 Jan 2013 10:18:17 +0100 Subject: Made auth_aclcheck always return int The returned type is important in particular when we deal with xmlrpc. Indeed, this value is directly returned to the client eg when the wiki.getAllPages method is queried. Currently the 'perms' attribute may be either an int or a string, and its up to the xmlrpc client to resolve it (although Dokuwiki's documentation only tells it can be an int). This patch makes sure we'll always return perms as int. --- inc/auth.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/auth.php b/inc/auth.php index 9c458338d..c68a699fe 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -580,7 +580,7 @@ function auth_aclcheck($id, $user, $groups) { } if($perm > -1) { //we had a match - return it - return $perm; + return (int) $perm; } } @@ -610,7 +610,7 @@ function auth_aclcheck($id, $user, $groups) { } //we had a match - return it if($perm != -1) { - return $perm; + return (int) $perm; } } //get next higher namespace -- cgit v1.2.3 From 7d1ae5e244b820e5c98558ef3bac0a764f63e692 Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Mon, 24 Dec 2012 23:47:46 +0100 Subject: Made xmlrpc putPage and appendPage consistent with documentation According to dokuwiki's documentation (https://www.dokuwiki.org/devel:xmlrpc#wikiputpage), those methods should return booleans, not int. --- inc/RemoteAPICore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php index 36c518881..fbbb7b48b 100644 --- a/inc/RemoteAPICore.php +++ b/inc/RemoteAPICore.php @@ -440,7 +440,7 @@ class RemoteAPICore { // run the indexer if page wasn't indexed yet idx_addPage($id); - return 0; + return true; } /** -- cgit v1.2.3 From e47e9c10c754a8b04f2f5fb07877e55863ffdea5 Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Sun, 6 Jan 2013 11:36:45 +0100 Subject: Fixed declared returned type --- inc/RemoteAPICore.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php index fbbb7b48b..c04a14f5c 100644 --- a/inc/RemoteAPICore.php +++ b/inc/RemoteAPICore.php @@ -48,7 +48,7 @@ class RemoteAPICore { 'public' => '1' ), 'dokuwiki.appendPage' => array( 'args' => array('string', 'string', 'array'), - 'return' => 'int', + 'return' => 'bool', 'doc' => 'Append text to a wiki page.' ), 'wiki.getPage' => array( 'args' => array('string'), @@ -102,7 +102,7 @@ class RemoteAPICore { 'name' => 'pageVersions' ), 'wiki.putPage' => array( 'args' => array('string', 'string', 'array'), - 'return' => 'int', + 'return' => 'bool', 'doc' => 'Saves a wiki page.' ), 'wiki.listLinks' => array( 'args' => array('string'), -- cgit v1.2.3 From 71e90b1c66985b02b7df3387930a5fa007ca9755 Mon Sep 17 00:00:00 2001 From: Osama Al-Kofahi Date: Sun, 6 Jan 2013 15:53:07 +0100 Subject: Arabic language update --- inc/lang/ar/lang.php | 6 ++++-- inc/lang/ar/mailwrap.html | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 inc/lang/ar/mailwrap.html (limited to 'inc') diff --git a/inc/lang/ar/lang.php b/inc/lang/ar/lang.php index 4ea8c6129..4928b3dbd 100644 --- a/inc/lang/ar/lang.php +++ b/inc/lang/ar/lang.php @@ -92,6 +92,7 @@ $lang['searchmedia_in'] = 'ابحث في %s'; $lang['txt_upload'] = 'اختر ملفاً للرفع'; $lang['txt_filename'] = 'رفع كـ (اختياري)'; $lang['txt_overwrt'] = 'اكتب على ملف موجود'; +$lang['maxuploadsize'] = 'الحجم الاقصى %s للملف'; $lang['lockedby'] = 'مقفلة حاليا لـ'; $lang['lockexpire'] = 'ينتهي القفل في'; $lang['js']['willexpire'] = 'سينتهي قفل تحرير هذه الصفحه خلال دقيقة.\nلتجنب التعارض استخدم زر المعاينة لتصفير مؤقت القفل.'; @@ -190,6 +191,7 @@ $lang['user_tools'] = 'أدوات المستخدم'; $lang['site_tools'] = 'أدوات الموقع'; $lang['page_tools'] = 'أدوات الصفحة'; $lang['skip_to_content'] = 'تجاوز إلى المحتوى'; +$lang['sidebar'] = 'العمود الجانبي'; $lang['mail_newpage'] = 'إضافة صفحة:'; $lang['mail_changed'] = 'تعديل صفحة:'; $lang['mail_subscribe_list'] = 'صفحات غيرت في النطاق:'; @@ -284,8 +286,8 @@ $lang['i_badhash'] = 'الملف dokuwiki.php غير مصنف أو (hash=%s)'; $lang['i_badval'] = 'القيمة %s غير شرعية أو فارغة'; $lang['i_success'] = 'الإعدادات تمت بنجاح، يرجى حذف الملف install.php الآن. -ثم تابع إلى دوكو ويكي الجديدة'; -$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; +ثم تابع إلى دوكو ويكي الجديدة'; +$lang['i_failure'] = 'بعض الأخطاء حدثت أثنا كتابة ملفات الإعدادات، عليك تعديلها يدوياً قبل أن تستطيع استخدام دوكو ويكي الجديدة'; $lang['i_policy'] = 'تصريح ACL مبدئي'; $lang['i_pol0'] = 'ويكي مفتوحة؛ أي القراءة والكتابة والتحميل مسموحة للجميع'; $lang['i_pol1'] = 'ويكي عامة؛ أي القراءة للجميع ولكن الكتابة والتحميل للمشتركين المسجلين فقط'; diff --git a/inc/lang/ar/mailwrap.html b/inc/lang/ar/mailwrap.html new file mode 100644 index 000000000..554e34510 --- /dev/null +++ b/inc/lang/ar/mailwrap.html @@ -0,0 +1,13 @@ + + +@TITLE@ + + + + +@HTMLBODY@ + +

    +تم انشاء هذا البريد الالكتروني بواسطة DokuWiki في @DOKUWIKIURL@. + + \ No newline at end of file -- cgit v1.2.3 From 7eaa7703a43b9f22f12be04e8580a3f2515fb146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=A5=E5=90=91=E5=B0=8F=E9=83=8E?= Date: Wed, 9 Jan 2013 20:30:03 +0100 Subject: Traditional Chinese language update --- inc/lang/zh-tw/adminplugins.txt | 2 +- inc/lang/zh-tw/backlinks.txt | 2 +- inc/lang/zh-tw/diff.txt | 2 +- inc/lang/zh-tw/edit.txt | 2 +- inc/lang/zh-tw/editrev.txt | 2 +- inc/lang/zh-tw/index.txt | 2 +- inc/lang/zh-tw/install.html | 6 +-- inc/lang/zh-tw/lang.php | 101 ++++++++++++++++++++------------------- inc/lang/zh-tw/locked.txt | 2 +- inc/lang/zh-tw/login.txt | 3 +- inc/lang/zh-tw/mailtext.txt | 2 +- inc/lang/zh-tw/norev.txt | 2 +- inc/lang/zh-tw/preview.txt | 2 +- inc/lang/zh-tw/pwconfirm.txt | 8 ++-- inc/lang/zh-tw/read.txt | 2 +- inc/lang/zh-tw/register.txt | 3 +- inc/lang/zh-tw/registermail.txt | 4 +- inc/lang/zh-tw/resendpwd.txt | 2 +- inc/lang/zh-tw/revisions.txt | 2 +- inc/lang/zh-tw/stopwords.txt | 10 ++-- inc/lang/zh-tw/subscr_digest.txt | 6 +-- inc/lang/zh-tw/subscr_form.txt | 2 +- inc/lang/zh-tw/subscr_list.txt | 6 +-- inc/lang/zh-tw/subscr_single.txt | 6 +-- inc/lang/zh-tw/updateprofile.txt | 2 +- inc/lang/zh-tw/uploadmail.txt | 4 +- 26 files changed, 93 insertions(+), 94 deletions(-) (limited to 'inc') diff --git a/inc/lang/zh-tw/adminplugins.txt b/inc/lang/zh-tw/adminplugins.txt index fb1999269..6d21ac2cd 100644 --- a/inc/lang/zh-tw/adminplugins.txt +++ b/inc/lang/zh-tw/adminplugins.txt @@ -1 +1 @@ -===== 外加插件 ===== \ No newline at end of file +===== 附加元件 ===== \ No newline at end of file diff --git a/inc/lang/zh-tw/backlinks.txt b/inc/lang/zh-tw/backlinks.txt index 5b36728e7..6a8bf8896 100644 --- a/inc/lang/zh-tw/backlinks.txt +++ b/inc/lang/zh-tw/backlinks.txt @@ -1,4 +1,4 @@ ====== 反向連結 ====== -這裡是引用、連結到目前頁面的頁面清單。 +這是引用、連結到目前頁面的頁面清單。 diff --git a/inc/lang/zh-tw/diff.txt b/inc/lang/zh-tw/diff.txt index 17fad7ba0..e2c05001f 100644 --- a/inc/lang/zh-tw/diff.txt +++ b/inc/lang/zh-tw/diff.txt @@ -1,3 +1,3 @@ ====== 差異處 ====== -這裏顯示二個版本的差異處。 \ No newline at end of file +這裏顯示兩個版本的差異處。 \ No newline at end of file diff --git a/inc/lang/zh-tw/edit.txt b/inc/lang/zh-tw/edit.txt index bbe5bb8ed..f6b74794f 100644 --- a/inc/lang/zh-tw/edit.txt +++ b/inc/lang/zh-tw/edit.txt @@ -1 +1 @@ -編輯本頁並按下''儲存''即可。可在[[wiki:syntax|維基語法]]找到語法說明。請只在能讓本文品質「**更好**」時才編輯。如果只是要測試,請使用 [[playground:playground|遊樂場]]。 +編輯本頁後,請按下「儲存」按鈕。若要參看語法說明,請到[[wiki:syntax|語法]]頁。請只在能讓本文品質**更好**時才編輯。如果只是要測試,請移玉步至 [[playground:playground|遊樂場]]。 \ No newline at end of file diff --git a/inc/lang/zh-tw/editrev.txt b/inc/lang/zh-tw/editrev.txt index 96f9a7c6a..98a800ab1 100644 --- a/inc/lang/zh-tw/editrev.txt +++ b/inc/lang/zh-tw/editrev.txt @@ -1,2 +1,2 @@ -**您目前載入的是本份文件的舊版!** 您如果存檔,這些資料就會被存成另一份。 +**您目前載入的是本份文件的舊版!** 您如果存檔,這些舊版資料就會變成最新版本。 ---- diff --git a/inc/lang/zh-tw/index.txt b/inc/lang/zh-tw/index.txt index bba277041..1b89e0c5d 100644 --- a/inc/lang/zh-tw/index.txt +++ b/inc/lang/zh-tw/index.txt @@ -1,3 +1,3 @@ ====== 站台地圖 ====== -這個站台地圖列出了所有允許的頁面,依 [[doku>namespaces|分類空間]] 排序。 \ No newline at end of file +這個站台地圖列出了所有允許的頁面,依 [[doku>namespaces|分類名稱]] 排序。 \ No newline at end of file diff --git a/inc/lang/zh-tw/install.html b/inc/lang/zh-tw/install.html index 2a8b1aac3..9a0d1dcd9 100644 --- a/inc/lang/zh-tw/install.html +++ b/inc/lang/zh-tw/install.html @@ -1,8 +1,8 @@

    本頁面旨在幫助您完成第一次安装和設定 Dokuwiki。關於安裝工具的更多訊息請參閱 官方文檔頁面

    -

    DokuWiki 使用普通檔案儲存維基頁面以及與頁面相關的訊息(例如:圖像,搜尋索引,修訂記錄等)。為了正常運作,DokuWiki 必須 擁有針對那些路徑和檔案的寫入權限。本安裝工具無法設定目錄權限,這通常要透過命令行、FTP 或您主機上的控制台(如cPanel)進行。

    +

    DokuWiki 使用普通檔案來儲存 wiki 頁面,以及與頁面相關的訊息(例如:圖像、搜尋索引、修訂記錄等)。為了正常運作,DokuWiki 必須 擁有針對那些路徑和檔案的寫入權限。本安裝工具無法設定目錄權限,這通常要透過命令行、FTP 或您主機上的控制台(如cPanel)進行。

    -

    本安裝工具將設定您的 DokuWiki 用於 ACL 的設定檔,它能讓管理員登入並使用「管理」功能來安裝插件、管理用户、管理訪問權限和其他設定設定。它並不是 DokuWiki 正常運作所必須,但安裝之後將更方便管理。

    +

    本安裝工具將設定您的 DokuWiki 用於 ACL 的設定檔,它能讓管理員登入並使用「管理」功能來安裝附加元件、管理使用者、管理訪問權限和其他設定設定。它並不是 DokuWiki 正常運作所必須,但安裝之後將更方便管理。

    -

    有經驗的用戶或有特殊需求的用戶請參閱更詳細的 安裝指南 +

    有經驗的或有特殊需求的使用者,請參閱更詳細的 安裝指南設定

    \ No newline at end of file diff --git a/inc/lang/zh-tw/lang.php b/inc/lang/zh-tw/lang.php index 9f380acb5..ddb35617e 100644 --- a/inc/lang/zh-tw/lang.php +++ b/inc/lang/zh-tw/lang.php @@ -11,6 +11,7 @@ * @author Danny Lin * @author Shuo-Ting Jian * @author syaoranhinata@gmail.com + * @author Ichirou Uchiki */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -19,7 +20,7 @@ $lang['doublequoteclosing'] = '”'; $lang['singlequoteopening'] = '‘'; $lang['singlequoteclosing'] = '’'; $lang['apostrophe'] = '’'; -$lang['btn_edit'] = '編修本頁'; +$lang['btn_edit'] = '編輯本頁'; $lang['btn_source'] = '顯示原始碼'; $lang['btn_show'] = '顯示頁面'; $lang['btn_create'] = '建立此頁'; @@ -34,7 +35,7 @@ $lang['btn_recent'] = '最近更新'; $lang['btn_upload'] = '上傳'; $lang['btn_cancel'] = '取消'; $lang['btn_index'] = '站台地圖'; -$lang['btn_secedit'] = '改這段'; +$lang['btn_secedit'] = '編輯此段'; $lang['btn_login'] = '登入'; $lang['btn_logout'] = '登出'; $lang['btn_admin'] = '管理選單'; @@ -61,40 +62,40 @@ $lang['newpass'] = '新密碼'; $lang['oldpass'] = '目前密碼'; $lang['passchk'] = '確認密碼'; $lang['remember'] = '記住帳號密碼'; -$lang['fullname'] = '真實姓名'; +$lang['fullname'] = '姓名'; $lang['email'] = '電郵'; $lang['profile'] = '使用者個人資料'; $lang['badlogin'] = '很抱歉,您的使用者名稱或密碼可能有錯誤。'; $lang['minoredit'] = '小修改'; $lang['draftdate'] = '草稿已自動存檔於'; -$lang['nosecedit'] = '頁面在這之間已被修改,過時的區段資料已載入全頁取代。'; +$lang['nosecedit'] = '在您編輯期間,其他使用者修改過本頁面。區段資料已逾時,因此系統載入了全頁,以取代之。'; $lang['regmissing'] = '很抱歉,所有欄位都要填寫。'; -$lang['reguexists'] = '很抱歉,本帳號已被註冊。'; -$lang['regsuccess'] = '使用者已建立,密碼已寄發至該 email。'; -$lang['regsuccess2'] = '使用者已建立。'; +$lang['reguexists'] = '很抱歉,有人已使用了這個帳號。'; +$lang['regsuccess'] = '使用者帳號已建立,密碼已寄發至該電郵。'; +$lang['regsuccess2'] = '使用者帳號已建立。'; $lang['regmailfail'] = '寄出密碼信似乎發生錯誤,請跟管理員聯絡!'; -$lang['regbadmail'] = '您輸入的 email 似乎不對,如果您認為是正確的,請與管理員聯絡。'; +$lang['regbadmail'] = '您輸入的電郵似乎不對,如果您認為是正確的,請與管理員聯絡。'; $lang['regbadpass'] = '兩次輸入的密碼不一致,請再試一次。'; $lang['regpwmail'] = '您的 DokuWiki 帳號密碼'; $lang['reghere'] = '您還沒有帳號嗎?註冊一個吧。'; -$lang['profna'] = '本維基不開放修改個人資料。'; -$lang['profnochange'] = '未做任何變更。'; +$lang['profna'] = '在本 wiki 上,不能修改個人資料。'; +$lang['profnochange'] = '並未作任何變更。'; $lang['profnoempty'] = '帳號或電郵地址不可空白!'; -$lang['profchanged'] = '個人資料已成功更新。'; +$lang['profchanged'] = '個人資料已更新。'; $lang['pwdforget'] = '忘記密碼了?索取新密碼!'; -$lang['resendna'] = '本維基不開放重寄密碼。'; +$lang['resendna'] = '本 wiki 並不支援重寄密碼。'; $lang['resendpwd'] = '設定新密碼供'; $lang['resendpwdmissing'] = '抱歉,您必須填寫所有欄位。'; $lang['resendpwdnouser'] = '抱歉,資料庫內找不到這個使用者。'; $lang['resendpwdbadauth'] = '抱歉,認證碼無效。請確認您使用了完整的確認連結。'; $lang['resendpwdconfirm'] = '確認連結已通過郵件發送給您了。'; $lang['resendpwdsuccess'] = '您的新密碼已寄出。'; -$lang['license'] = '若未特別註明,此維基上的內容都是採用以下授權方式:'; +$lang['license'] = '若無特別註明,本 wiki 上的內容都是採用以下授權方式:'; $lang['licenseok'] = '注意:編輯此頁面表示您已同意以下的授權方式:'; $lang['searchmedia'] = '搜尋檔名:'; $lang['searchmedia_in'] = '在 %s 裏搜尋'; $lang['txt_upload'] = '請選擇要上傳的檔案'; -$lang['txt_filename'] = '請輸入要存在維基內的檔案名稱 (非必要)'; +$lang['txt_filename'] = '請輸入要上傳至本 wiki 的檔案名稱 (非必要)'; $lang['txt_overwrt'] = '是否要覆蓋原有檔案'; $lang['maxuploadsize'] = '每個上傳檔案不可大於 %s 。'; $lang['lockedby'] = '目前已被下列人員鎖定'; @@ -135,7 +136,7 @@ $lang['js']['media_diff'] = '檢視差異:'; $lang['js']['media_diff_both'] = '並排'; $lang['js']['media_diff_opacity'] = '重疊'; $lang['js']['media_diff_portions'] = '滑動'; -$lang['js']['media_select'] = '選擇檔案…'; +$lang['js']['media_select'] = '選擇檔案……'; $lang['js']['media_upload_btn'] = '上傳'; $lang['js']['media_done_btn'] = '完成'; $lang['js']['media_drop'] = '拖拉檔案到此上傳'; @@ -145,27 +146,27 @@ $lang['rssfailed'] = '擷取 RSS 饋送檔時發生錯誤:'; $lang['nothingfound'] = '沒找到任何結果。'; $lang['mediaselect'] = '媒體檔案'; $lang['fileupload'] = '上傳媒體檔案'; -$lang['uploadsucc'] = '上傳成功'; -$lang['uploadfail'] = '上傳失敗。似乎是權限錯誤?'; +$lang['uploadsucc'] = '已上傳'; +$lang['uploadfail'] = '無法上傳。是否因權限錯誤?'; $lang['uploadwrong'] = '拒絕上傳。這個副檔名被禁止了!'; $lang['uploadexist'] = '檔案已存在,未處理。'; $lang['uploadbadcontent'] = '上傳檔案的內容不符合 %s 檔的副檔名。'; -$lang['uploadspam'] = '這次的上傳被垃圾訊息黑名單阻檔了。'; -$lang['uploadxss'] = '這次的上傳因可能的惡意的內容而被阻檔。'; -$lang['uploadsize'] = '上傳的檔案太大了 (最大:%s)'; +$lang['uploadspam'] = '是次上傳被垃圾訊息黑名單阻檔了。'; +$lang['uploadxss'] = '因可能含有惡意內容,是次上傳已被阻檔。'; +$lang['uploadsize'] = '上傳的檔案太大了 (最大為:%s)'; $lang['deletesucc'] = '檔案 "%s" 已刪除。'; $lang['deletefail'] = '檔案 "%s" 無法刪除,請檢查權限定。'; -$lang['mediainuse'] = '檔案 "%s" 未刪除,因為它正被使用。'; -$lang['namespaces'] = '分類空間'; +$lang['mediainuse'] = '檔案 "%s" 仍在使用,並未刪除。'; +$lang['namespaces'] = '分類名稱'; $lang['mediafiles'] = '可用的檔案有'; $lang['accessdenied'] = '您不可以檢視此頁面。'; $lang['mediausage'] = '使用以下的語法來連結此檔案:'; $lang['mediaview'] = '檢視原始檔案'; $lang['mediaroot'] = 'root'; -$lang['mediaupload'] = '上傳檔案至目前的分類空間。要建立子分類空間,將其名稱加在「上傳並重命名為」檔案名的前面,並用英文冒號隔開。'; +$lang['mediaupload'] = '上傳檔案至目前分類名稱之下。要建立子分類名稱,請將其名稱加在「上傳並重命名為」檔案名的前面,並用英文冒號隔開。'; $lang['mediaextchange'] = '檔案類型已由 .%s 變更為 .%s !'; $lang['reference'] = '引用到本頁的,合計有'; -$lang['ref_inuse'] = '此檔案無法刪除,因為它正被以下頁面使用:'; +$lang['ref_inuse'] = '此檔案無法刪除,因以下頁面正在使用它:'; $lang['ref_hidden'] = '一些參考內容位於您沒有讀取權限的頁面中'; $lang['hits'] = '個符合'; $lang['quickhits'] = '符合的頁面名稱'; @@ -175,12 +176,12 @@ $lang['yours'] = '您的版本'; $lang['diff'] = '顯示與目前版本的差異'; $lang['diff2'] = '顯示選擇版本間的差異'; $lang['difflink'] = '連向這個比對檢視'; -$lang['diff_type'] = '檢視差異:'; +$lang['diff_type'] = '檢視差異:'; $lang['diff_inline'] = '行內'; $lang['diff_side'] = '並排'; $lang['line'] = '行'; $lang['breadcrumb'] = '足跡'; -$lang['youarehere'] = '您在這裡'; +$lang['youarehere'] = '您在這裏'; $lang['lastmod'] = '上一次變更'; $lang['by'] = '由'; $lang['deleted'] = '移除'; @@ -188,17 +189,17 @@ $lang['created'] = '建立'; $lang['restored'] = '恢復為舊版'; $lang['external_edit'] = '外部編輯'; $lang['summary'] = '編輯摘要'; -$lang['noflash'] = '顯示此內容需要 Adobe Flash Plugin'; +$lang['noflash'] = '顯示此內容需要 Adobe Flash 附加元件。'; $lang['download'] = '下載程式碼片段'; $lang['tools'] = '工具'; -$lang['user_tools'] = '用戶工具'; +$lang['user_tools'] = '使用者工具'; $lang['site_tools'] = '網站工具'; $lang['page_tools'] = '頁面工具'; $lang['skip_to_content'] = '跳至內容'; $lang['sidebar'] = '側欄'; $lang['mail_newpage'] = '增加的頁面:'; $lang['mail_changed'] = '變更的頁面:'; -$lang['mail_subscribe_list'] = '分類空間中更動的頁面:'; +$lang['mail_subscribe_list'] = '分類名稱中變更的頁面:'; $lang['mail_new_user'] = '新使用者:'; $lang['mail_upload'] = '已上傳檔案:'; $lang['changes_type'] = '檢視最近更新類型'; @@ -229,10 +230,10 @@ $lang['qb_media'] = '加入圖片或檔案'; $lang['qb_sig'] = '插入簽名'; $lang['qb_smileys'] = '表情符號'; $lang['qb_chars'] = '特殊字元'; -$lang['upperns'] = '前往父分類空間'; +$lang['upperns'] = '前往父分類名稱'; $lang['admin_register'] = '新增使用者'; $lang['metaedit'] = '編輯後設資料'; -$lang['metasaveerr'] = '後設資料寫入失敗'; +$lang['metasaveerr'] = '後設資料無法寫入'; $lang['metasaveok'] = '後設資料已儲存'; $lang['img_backto'] = '回上一頁'; $lang['img_title'] = '標題'; @@ -253,9 +254,9 @@ $lang['subscr_subscribe_error'] = '將 %s 加入至 %s 的訂閱列表時發生 $lang['subscr_subscribe_noaddress'] = '沒有與您登入相關的地址,無法將您加入訂閱列表'; $lang['subscr_unsubscribe_success'] = '已將 %s 移除自 %s 的訂閱列表'; $lang['subscr_unsubscribe_error'] = '將 %s 移除自 %s 的訂閱列表時發生錯誤'; -$lang['subscr_already_subscribed'] = '%s 已經被 %s 訂閱了'; -$lang['subscr_not_subscribed'] = '%s 尚未被 %s 訂閱'; -$lang['subscr_m_not_subscribed'] = '您尚未訂閱目前的頁面或分類空間。'; +$lang['subscr_already_subscribed'] = '%s 已經獲 %s 訂閱了'; +$lang['subscr_not_subscribed'] = '%s 尚未獲 %s 訂閱'; +$lang['subscr_m_not_subscribed'] = '您尚未訂閱目前的頁面或分類名稱。'; $lang['subscr_m_new_header'] = '加入訂閱'; $lang['subscr_m_current_header'] = '目前訂閱'; $lang['subscr_m_unsubscribe'] = '取消訂閱'; @@ -264,34 +265,34 @@ $lang['subscr_m_receive'] = '接收'; $lang['subscr_style_every'] = '每次更改都發送信件'; $lang['subscr_style_digest'] = '對每個頁面發送更改的摘要信件 (每 %.2f 天)'; $lang['subscr_style_list'] = '自上次發信以來更改的頁面的列表 (每 %.2f 天)'; -$lang['authmodfailed'] = '帳號認證的設定不正確,請通知該維基管理員。'; -$lang['authtempfail'] = '帳號認證目前暫不提供,若本狀況持續發生的話,請通知該維基管理員。'; +$lang['authmodfailed'] = '帳號認證的設定不正確,請通知該本 wiki 管理員。'; +$lang['authtempfail'] = '帳號認證目前暫不提供。若本狀況持續,請通知本 wiki 管理員。'; $lang['authpwdexpire'] = '您的密碼將在 %d 天內到期,請馬上更換新密碼。'; $lang['i_chooselang'] = '選擇您的語系'; $lang['i_installer'] = 'DokuWiki 安裝工具'; -$lang['i_wikiname'] = '維基名稱'; +$lang['i_wikiname'] = '本 wiki 的名稱'; $lang['i_enableacl'] = '啟用 ACL (建議)'; -$lang['i_superuser'] = '超級用戶'; +$lang['i_superuser'] = '超級使用者'; $lang['i_problems'] = '安裝程式發現如下的問題。您必須修正它們才能繼續。'; $lang['i_modified'] = '出於安全考量,本腳本只能用於安裝全新且未修改的 Dokuwiki。 您可以重新解壓下載的封包或查閱完整的Dokuwiki 安裝指南'; $lang['i_funcna'] = 'PHP 函數 %s 無法使用。也許您的主機供應者基於某些理由停用了它?'; $lang['i_phpver'] = '您的 PHP 版本 %s 比需要的版本 %s 還低。您必須更新您的PHP。'; -$lang['i_permfail'] = '%s 無法被 DokuWiki 寫入。您必須修正該目錄的權限!'; +$lang['i_permfail'] = '%s 無法經由 DokuWiki 寫入。您必須修正該目錄的權限!'; $lang['i_confexists'] = '%s 已經存在'; $lang['i_writeerr'] = '無法建立 %s。您必須檢查目錄/檔案的權限並手動建立該檔案。'; -$lang['i_badhash'] = '無法辨識或被變更的 dokuwiki.php (hash=%s)'; -$lang['i_badval'] = '%s - 非法或空白的值'; -$lang['i_success'] = '設定已成功完成。您現在可以刪除 install.php 檔案。繼續到 -您的新 DokuWiki.'; -$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; +$lang['i_badhash'] = '無法辨識或已遭修改的 dokuwiki.php (hash=%s)'; +$lang['i_badval'] = '%s —— 非法或空白的值'; +$lang['i_success'] = '設定已完成。您現在可以刪除 install.php 檔案。繼續到 +您的新 DokuWiki.'; +$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用您的新 Dokuwiki 之前手動修正它們。'; $lang['i_policy'] = '初步的 ACL 政策'; -$lang['i_pol0'] = '開放的維基 (任何人可讀取、寫入、上傳)'; -$lang['i_pol1'] = '公開的維基 (任何人可讀取,註冊使用者可寫入與上傳)'; -$lang['i_pol2'] = '封閉的維基 (只有註冊使用者可讀取、寫入、上傳)'; +$lang['i_pol0'] = '開放的 wiki (任何人可讀取、寫入、上傳)'; +$lang['i_pol1'] = '公開的 wiki (任何人可讀取,註冊使用者可寫入與上傳)'; +$lang['i_pol2'] = '封閉的 wiki (只有註冊使用者可讀取、寫入、上傳)'; $lang['i_retry'] = '重試'; $lang['i_license'] = '請選擇您想要的內容發布許可協議:'; -$lang['recent_global'] = '您正在閱讀分類空間: %s 中的變更。您亦可觀看整個維基的最近更新。'; +$lang['recent_global'] = '您正在閱讀分類名稱: %s 中的變更。您亦可觀看本 wiki 所有的最近更新。'; $lang['years'] = '%d 年前'; $lang['months'] = '%d 個月前'; $lang['weeks'] = '%d 週前'; @@ -299,7 +300,7 @@ $lang['days'] = '%d 天前'; $lang['hours'] = '%d 個小時前'; $lang['minutes'] = '%d 分鐘前'; $lang['seconds'] = '%s 秒鐘前'; -$lang['wordblock'] = '您的更改沒有被儲存,因为它包含被阻擋的文字 (垃圾訊息)。'; +$lang['wordblock'] = '無法儲存您的更改,因為它含有受阻擋的文字 (垃圾訊息)。'; $lang['media_uploadtab'] = '上傳'; $lang['media_searchtab'] = '搜尋'; $lang['media_file'] = '檔案'; @@ -310,7 +311,7 @@ $lang['media_list_thumbs'] = '縮圖'; $lang['media_list_rows'] = '列表'; $lang['media_sort_name'] = '名稱'; $lang['media_sort_date'] = '日期'; -$lang['media_namespaces'] = '選擇分類空間'; +$lang['media_namespaces'] = '選擇分類名稱'; $lang['media_files'] = '在 %s 中的檔案'; $lang['media_upload'] = '上傳至 %s'; $lang['media_search'] = '在 %s 中搜尋'; diff --git a/inc/lang/zh-tw/locked.txt b/inc/lang/zh-tw/locked.txt index e13fdc890..819e59e2f 100644 --- a/inc/lang/zh-tw/locked.txt +++ b/inc/lang/zh-tw/locked.txt @@ -1,3 +1,3 @@ ====== 頁面鎖定 ====== -本頁目前正由其他使用者編修中,您必須等他完成編輯或等鎖定時間過去。 +其他使用者正在編輯本頁,您必須等他完成編輯或等鎖定時間過去。 diff --git a/inc/lang/zh-tw/login.txt b/inc/lang/zh-tw/login.txt index 058cc5712..b82f08a20 100644 --- a/inc/lang/zh-tw/login.txt +++ b/inc/lang/zh-tw/login.txt @@ -1,5 +1,4 @@ ====== 登入 ====== -您尚未登入,請輸入您的使用者名稱和密碼。 另外,瀏覽器需要打開 cookies 設定以進行登入。 - +您尚未登入,請輸入您的使用者名稱和密碼。 另外,瀏覽器需要啟用 cookies 以登入本 wiki。 diff --git a/inc/lang/zh-tw/mailtext.txt b/inc/lang/zh-tw/mailtext.txt index 3b6ece377..2a402c2fb 100644 --- a/inc/lang/zh-tw/mailtext.txt +++ b/inc/lang/zh-tw/mailtext.txt @@ -2,7 +2,7 @@ 日期 : @DATE@ 瀏覽器 : @BROWSER@ -IP位址 : @IPADDRESS@ +IP 位址 : @IPADDRESS@ 主機名稱 : @HOSTNAME@ 舊版本 : @OLDPAGE@ 新版本 : @NEWPAGE@ diff --git a/inc/lang/zh-tw/norev.txt b/inc/lang/zh-tw/norev.txt index bd1c7a623..2a32ba6a8 100644 --- a/inc/lang/zh-tw/norev.txt +++ b/inc/lang/zh-tw/norev.txt @@ -1,3 +1,3 @@ ====== 無此版本 ====== -該版本的文件不存在。請用 「舊版」按鈕檢視該文件所有舊版本清單。 \ No newline at end of file +該版本的文件不存在。請用「舊版」按鈕檢視該文件所有舊版本清單。 \ No newline at end of file diff --git a/inc/lang/zh-tw/preview.txt b/inc/lang/zh-tw/preview.txt index c68f94819..95d4b10e1 100644 --- a/inc/lang/zh-tw/preview.txt +++ b/inc/lang/zh-tw/preview.txt @@ -1,4 +1,4 @@ ====== 預覽 ====== -以下是預覽該文件的狀態。請記住:**它還沒被儲存喔**! +以下是該文件的預覽。請記住:**您還未儲存它**! diff --git a/inc/lang/zh-tw/pwconfirm.txt b/inc/lang/zh-tw/pwconfirm.txt index 994367980..fb8a5cb82 100644 --- a/inc/lang/zh-tw/pwconfirm.txt +++ b/inc/lang/zh-tw/pwconfirm.txt @@ -1,13 +1,13 @@ @FULLNAME@ 您好! -有人為您在 @DOKUWIKIURL@ 註冊的使用者 @TITLE@ 請求新密碼 +感謝您在 @TITLE@ ( @DOKUWIKIURL@ ) 註冊了使用者帳號。我們收到請求,希望能允許此帳號使用新密碼。 -如果您沒有請求新密碼,請忽略這封郵件。 +如果您沒有發送此請求,請忽略這封郵件。 -要確認使用新密碼,請拜訪以下的連結。 +若您真的要使用新密碼,請拜訪以下的連結。 @CONFIRM@ -- 本信件由以下 DokuWiki 站台產生: -@DOKUWIKIURL@ +@DOKUWIKIURL@ \ No newline at end of file diff --git a/inc/lang/zh-tw/read.txt b/inc/lang/zh-tw/read.txt index a8305611f..4a472cd0d 100644 --- a/inc/lang/zh-tw/read.txt +++ b/inc/lang/zh-tw/read.txt @@ -1 +1 @@ -本頁是唯讀的,您可以看到原始碼,但不能更動它。您如果覺得這是誤判,請詢問管理員。 \ No newline at end of file +本頁是唯讀的,您可以看到原始碼,但不能更動它。您如果覺得它不應被鎖上,請詢問管理員。 \ No newline at end of file diff --git a/inc/lang/zh-tw/register.txt b/inc/lang/zh-tw/register.txt index 0da401ccd..3ff68dee3 100644 --- a/inc/lang/zh-tw/register.txt +++ b/inc/lang/zh-tw/register.txt @@ -1,4 +1,3 @@ ====== 註冊新使用者 ====== -填寫以下資料以註冊維基帳號,請確定您提供的是**合法的 email 地址**-如果這裡沒要求您填寫密碼,您的新密碼會自動產生並寄送到該地址。登錄名稱必須是合法的[[doku>pagename|頁面名稱]]。 - +若要註冊本 wiki 的帳號,請填寫下列資料。請確定您提供的是**合法的電郵地址**。如果您不必填寫密碼,系統就會為您自動產生登入密碼,並寄送到該電郵地址。登錄名稱須符合正確[[doku>pagename|頁面名稱]]之條件。 diff --git a/inc/lang/zh-tw/registermail.txt b/inc/lang/zh-tw/registermail.txt index 489e3f9d3..fb4951d60 100644 --- a/inc/lang/zh-tw/registermail.txt +++ b/inc/lang/zh-tw/registermail.txt @@ -2,11 +2,11 @@ 帳號 : @NEWUSER@ 姓名 : @NEWNAME@ -E-mail : @NEWEMAIL@ +電郵 : @NEWEMAIL@ 日期 : @DATE@ 瀏覽器 : @BROWSER@ -IP位址 : @IPADDRESS@ +IP 位址 : @IPADDRESS@ 主機名稱 : @HOSTNAME@ -- diff --git a/inc/lang/zh-tw/resendpwd.txt b/inc/lang/zh-tw/resendpwd.txt index 3dbfad750..46078a348 100644 --- a/inc/lang/zh-tw/resendpwd.txt +++ b/inc/lang/zh-tw/resendpwd.txt @@ -1,3 +1,3 @@ ====== 寄送新密碼 ====== -請在以下欄位輸入您的帳號,新密碼將會寄送到您註冊時填寫的 email 地址。 +請在以下欄位輸入您的帳號,新密碼將會寄送到您註冊時填寫的電郵地址。 \ No newline at end of file diff --git a/inc/lang/zh-tw/revisions.txt b/inc/lang/zh-tw/revisions.txt index 479705b95..4818839ac 100644 --- a/inc/lang/zh-tw/revisions.txt +++ b/inc/lang/zh-tw/revisions.txt @@ -1,3 +1,3 @@ ====== 舊版 ====== -以下是該文件的舊版本。如要恢復成某個舊版次,就點下它,然後按「編修本頁」,並存檔起來就可以了。 +以下是該文件的舊版本。如要恢復成某個舊版次,就點下它,然後按「編輯本頁」,並存檔起來就可以了。 diff --git a/inc/lang/zh-tw/stopwords.txt b/inc/lang/zh-tw/stopwords.txt index 55b67ed16..e549250bd 100644 --- a/inc/lang/zh-tw/stopwords.txt +++ b/inc/lang/zh-tw/stopwords.txt @@ -1,9 +1,9 @@ -# 這檔是製作索引檔(index)時不要列入的關鍵字,格式為每字(詞)就使用一行。 -# 在修改時,請注意要用 UNIX 格式的換行符號(newline)處理,而非 DOS 的 CR-LR 喔 -# (如果在 MS Windows 環境使用的話,可使用 vim win32版 或 UltraEdit或其他類似編輯器修改) +# 本清單列出製作索引檔 (index) 時不要列入的關鍵字,格式為每字 (詞) 佔一行。 +# 在修改本清單時,請注意要用 UNIX 格式的換行符號 (newline) 處理,而非 DOS 的 CR-LR 。 +# (如果在 MS Windows 環境使用的話,可使用 vim win32 版、 UltraEdit 或其他類似編輯器修改。) # -# 還有,不必把小於 3 個字元(英數字元)都包括進來。 -# 目前本清單的內容是以 http://www.ranks.nl/stopwords/ 為基礎而發展的。 +# 還有,不必把小於 3 個字元 (英數字元) 都包括進來。 +# 目前本清單的內容是以 http://www.ranks.nl/stopwords/ 為基礎,發展而成的。 about are and diff --git a/inc/lang/zh-tw/subscr_digest.txt b/inc/lang/zh-tw/subscr_digest.txt index a0f2be73e..a17a0551d 100644 --- a/inc/lang/zh-tw/subscr_digest.txt +++ b/inc/lang/zh-tw/subscr_digest.txt @@ -1,6 +1,6 @@ 您好! -維基 @TITLE@ 的頁面 @PAGE@ 已更改。 +本 wiki ( @TITLE@ ) 的頁面 @PAGE@ 已更改。 更改內容如下: -------------------------------------------------------- @@ -10,9 +10,9 @@ 舊版本:@OLDPAGE@ 新版本:@NEWPAGE@ -要取消頁面提醒,請登入維基 @DOKUWIKIURL@ +要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@ 然後拜訪 @SUBSCRIBE@ -並取消訂閱頁面或分類空間的更改。 +並取消訂閱頁面或分類名稱的更改。 -- 本信件由以下 DokuWiki 站台產生: diff --git a/inc/lang/zh-tw/subscr_form.txt b/inc/lang/zh-tw/subscr_form.txt index 394d4cbad..ba3f16113 100644 --- a/inc/lang/zh-tw/subscr_form.txt +++ b/inc/lang/zh-tw/subscr_form.txt @@ -1,3 +1,3 @@ ====== 訂閱管理 ====== -這個頁面允許您管理在目前頁面和分類空間的訂閱。 \ No newline at end of file +在此頁裏,您可以管理在目前頁面及分類名稱之訂閱。 \ No newline at end of file diff --git a/inc/lang/zh-tw/subscr_list.txt b/inc/lang/zh-tw/subscr_list.txt index 078eae63e..ea82dd35d 100644 --- a/inc/lang/zh-tw/subscr_list.txt +++ b/inc/lang/zh-tw/subscr_list.txt @@ -1,15 +1,15 @@ 您好! -維基 @TITLE@ 的 @PAGE@ 分類空間的頁面已更改。 +本 wiki ( @TITLE@ ) 的 @PAGE@ 分類名稱頁面已更改。 更改內容如下: -------------------------------------------------------- @DIFF@ -------------------------------------------------------- -要取消頁面提醒,請登入維基 @DOKUWIKIURL@ +要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@ 然後拜訪 @SUBSCRIBE@ -並取消訂閱頁面或分類空間的更改。 +並取消訂閱頁面或分類名稱的更改。 -- 本信件由以下 DokuWiki 站台產生: diff --git a/inc/lang/zh-tw/subscr_single.txt b/inc/lang/zh-tw/subscr_single.txt index 5128140d0..14d87bb73 100644 --- a/inc/lang/zh-tw/subscr_single.txt +++ b/inc/lang/zh-tw/subscr_single.txt @@ -1,6 +1,6 @@ 您好! -維基 @TITLE@ 的頁面 @PAGE@ 已更改。 +本 wiki ( @TITLE@ ) 的頁面 @PAGE@ 已更改。 更改內容如下: -------------------------------------------------------- @@ -13,9 +13,9 @@ 舊版本 : @OLDPAGE@ 新版本 : @NEWPAGE@ -要取消頁面提醒,請登入維基 @DOKUWIKIURL@ +要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@ 然後拜訪 @NEWPAGE@ -並取消訂閱頁面或分類空間的更改。 +並取消訂閱頁面或分類名稱的更改。 -- 本信件由以下 DokuWiki 站台產生: diff --git a/inc/lang/zh-tw/updateprofile.txt b/inc/lang/zh-tw/updateprofile.txt index 47f2ae1c4..a7a2ad803 100644 --- a/inc/lang/zh-tw/updateprofile.txt +++ b/inc/lang/zh-tw/updateprofile.txt @@ -1,3 +1,3 @@ ====== 更新個人資料 ====== -您只需變更想更新的欄位就好,帳號名稱不能變更。 \ No newline at end of file +您只需修改想更新的欄位就好,帳號名稱不能變更。 \ No newline at end of file diff --git a/inc/lang/zh-tw/uploadmail.txt b/inc/lang/zh-tw/uploadmail.txt index b4c1311ba..87bac7c9a 100644 --- a/inc/lang/zh-tw/uploadmail.txt +++ b/inc/lang/zh-tw/uploadmail.txt @@ -3,7 +3,7 @@ 檔名 : @MEDIA@ 日期 : @DATE@ 瀏覽器 : @BROWSER@ -IP位址 : @IPADDRESS@ +IP 位址 : @IPADDRESS@ 主機名稱 : @HOSTNAME@ 大小 : @SIZE@ MIME類型 : @MIME@ @@ -11,4 +11,4 @@ MIME類型 : @MIME@ -- 本信件由以下 DokuWiki 站台產生: -@DOKUWIKIURL@ +@DOKUWIKIURL@ \ No newline at end of file -- cgit v1.2.3 From ffb060ea6b02211074dfc314f8651936065b3f7f Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Wed, 9 Jan 2013 21:09:42 +0100 Subject: show filesize of local file links in tooltip --- inc/parser/xhtml.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc') diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php index 68b92ca43..71f3aa4bf 100644 --- a/inc/parser/xhtml.php +++ b/inc/parser/xhtml.php @@ -803,6 +803,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer { $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); $link['class'] .= ' mediafile mf_'.$class; $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true); + $link['title'] .= ' (' . filesize_h(filesize(mediaFN($src))).')'; } if($hash) $link['url'] .= '#'.$hash; -- cgit v1.2.3 From c647387e09a52c1a67a72b8923ca4de06f8555cf Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Wed, 9 Jan 2013 22:26:22 +0100 Subject: Check search limit depth with an inequality Otherwise, when we check for equality, the test fails if the search start beneath the max depth. Eg: if I have the page ns1:ns2:mypage in my wiki, and launch the xmlrpc query getPagelist("ns1", "depth => 1"), without this patch, it retrieves mypage --- inc/search.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 1cecfd5ec..e18777063 100644 --- a/inc/search.php +++ b/inc/search.php @@ -247,11 +247,16 @@ function search_pagename(&$data,$base,$file,$type,$lvl,$opts){ * @author Andreas Gohr */ function search_allpages(&$data,$base,$file,$type,$lvl,$opts){ + if(isset($opts['depth'])){ + $parts = explode('/',ltrim($file,'/')); + if(($type == 'd' && count($parts) > $opts['depth']) + || ($type != 'd' && count($parts) > $opts['depth'] + 1)){ + return false; // depth reached + } + } + //we do nothing with directories if($type == 'd'){ - if(!$opts['depth']) return true; // recurse forever - $parts = explode('/',ltrim($file,'/')); - if(count($parts) == $opts['depth']) return false; // depth reached return true; } -- cgit v1.2.3 From 8451f4ad87cd104e80eb42532adebd7b2882e62e Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Thu, 10 Jan 2013 22:55:43 +0100 Subject: If depth option is set to 0, we should recurse forever At least if we want to be consistent with search_universal --- inc/search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index e18777063..53bd240e8 100644 --- a/inc/search.php +++ b/inc/search.php @@ -247,7 +247,7 @@ function search_pagename(&$data,$base,$file,$type,$lvl,$opts){ * @author Andreas Gohr */ function search_allpages(&$data,$base,$file,$type,$lvl,$opts){ - if(isset($opts['depth'])){ + if(isset($opts['depth']) && $opts['depth']){ $parts = explode('/',ltrim($file,'/')); if(($type == 'd' && count($parts) > $opts['depth']) || ($type != 'd' && count($parts) > $opts['depth'] + 1)){ -- cgit v1.2.3 From f8591821bac23569788706524f8d6b14bd73775c Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Thu, 10 Jan 2013 08:02:26 +0100 Subject: Don't remove page version from result if uneeded It may happen when the page has few versions --- inc/RemoteAPICore.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php index c04a14f5c..fef0b81dd 100644 --- a/inc/RemoteAPICore.php +++ b/inc/RemoteAPICore.php @@ -654,7 +654,9 @@ class RemoteAPICore { if(count($revisions)>0 && $first==0) { array_unshift($revisions, ''); // include current revision - array_pop($revisions); // remove extra log entry + if ( count($revisions) > $conf['recent'] ){ + array_pop($revisions); // remove extra log entry + } } if(count($revisions) > $conf['recent']) { -- cgit v1.2.3 From 187b13eb2e4f044659ad056a038b7aed65033579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Prat?= Date: Fri, 18 Jan 2013 10:07:50 +0100 Subject: Catalan language update --- inc/lang/ca/lang.php | 129 +++++++++++++++++++++++++++++++++++------- inc/lang/ca/mailwrap.html | 13 +++++ inc/lang/ca/resetpwd.txt | 3 + inc/lang/ca/subscr_digest.txt | 21 +++++++ inc/lang/ca/subscr_form.txt | 3 + inc/lang/ca/subscr_list.txt | 21 +++++++ 6 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 inc/lang/ca/mailwrap.html create mode 100644 inc/lang/ca/resetpwd.txt create mode 100644 inc/lang/ca/subscr_digest.txt create mode 100644 inc/lang/ca/subscr_form.txt create mode 100644 inc/lang/ca/subscr_list.txt (limited to 'inc') diff --git a/inc/lang/ca/lang.php b/inc/lang/ca/lang.php index 0fd88ec39..cb2b64686 100644 --- a/inc/lang/ca/lang.php +++ b/inc/lang/ca/lang.php @@ -5,6 +5,7 @@ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Carles Bellver * @author Carles Bellver + * @author daniel@6temes.cat */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -27,7 +28,7 @@ $lang['btn_revs'] = 'Revisions anteriors'; $lang['btn_recent'] = 'Canvis recents'; $lang['btn_upload'] = 'Penja'; $lang['btn_cancel'] = 'Cancel·la'; -$lang['btn_index'] = 'Índex'; +$lang['btn_index'] = 'Mapa del lloc'; $lang['btn_secedit'] = 'Edita'; $lang['btn_login'] = 'Entra'; $lang['btn_logout'] = 'Surt'; @@ -38,14 +39,15 @@ $lang['btn_back'] = 'Enrere'; $lang['btn_backlink'] = 'Què hi enllaça'; $lang['btn_backtomedia'] = 'Torna a la selecció de fitxers'; $lang['btn_subscribe'] = 'Subscripció a canvis d\'aquesta pàgina'; -$lang['btn_unsubscribe'] = 'Cancel·la subscripció a pàgina'; $lang['btn_profile'] = 'Actualització del perfil'; $lang['btn_reset'] = 'Reinicia'; +$lang['btn_resendpwd'] = 'Estableix una nova contrasenya'; $lang['btn_draft'] = 'Edita esborrany'; $lang['btn_recover'] = 'Recupera esborrany'; $lang['btn_draftdel'] = 'Suprimeix esborrany'; $lang['btn_revert'] = 'Restaura'; $lang['btn_register'] = 'Registra\'m'; +$lang['btn_apply'] = 'Aplica'; $lang['loggedinas'] = 'Heu entrat com'; $lang['user'] = 'Nom d\'usuari'; $lang['pass'] = 'Contrasenya'; @@ -75,6 +77,7 @@ $lang['profnoempty'] = 'No es pot deixar en blanc el nom o l\'adreça $lang['profchanged'] = 'El perfil d\'usuari s\'ha actualitzat correctament.'; $lang['pwdforget'] = 'Heu oblidat la contrasenya? Podeu obtenir-ne una de nova.'; $lang['resendna'] = 'Aquest wiki no permet tornar a enviar la contrasenya.'; +$lang['resendpwd'] = 'Estableix una nova contrasenya per'; $lang['resendpwdmissing'] = 'Heu d\'emplenar tots els camps.'; $lang['resendpwdnouser'] = 'No s\'ha pogut trobar aquest usuari a la base de dades.'; $lang['resendpwdbadauth'] = 'Aquest codi d\'autenticació no és vàlid. Assegureu-vos d\'utilitzar l\'enllaç de confirmació complet.'; @@ -87,10 +90,52 @@ $lang['searchmedia_in'] = 'Cerca en: %s'; $lang['txt_upload'] = 'Trieu el fitxer que voleu penjar'; $lang['txt_filename'] = 'Introduïu el nom wiki (opcional)'; $lang['txt_overwrt'] = 'Sobreescriu el fitxer actual'; +$lang['maxuploadsize'] = 'Puja com a màxim %s per arxiu.'; $lang['lockedby'] = 'Actualment blocat per:'; $lang['lockexpire'] = 'Venciment del blocatge:'; -$lang['js']['willexpire'] = 'El blocatge per a editar aquesta pàgina venç d\'aquí a un minut.\nUtilitzeu la visualització prèvia per reiniciar el rellotge i evitar conflictes.'; -$lang['js']['notsavedyet'] = "Heu fet canvis que es perdran si no els deseu.\nVoleu continuar?"; +$lang['js']['willexpire'] = 'El blocatge per a editar aquesta pàgina venç d\'aquí a un minut.\nUtilitzeu la visualització prèvia per reiniciar el rellotge i evitar conflictes.'; +$lang['js']['notsavedyet'] = 'Heu fet canvis que es perdran si no els deseu. +Voleu continuar?'; +$lang['js']['searchmedia'] = 'Cerca fitxers'; +$lang['js']['keepopen'] = 'Manté la finestra oberta'; +$lang['js']['hidedetails'] = 'Oculta detalls'; +$lang['js']['mediatitle'] = 'Propietats de l\'enllaç'; +$lang['js']['mediadisplay'] = 'Tipus d\'enllaç'; +$lang['js']['mediaalign'] = 'Alineació'; +$lang['js']['mediasize'] = 'Mida de la imatge'; +$lang['js']['mediatarget'] = 'Destí de l\'enllaç'; +$lang['js']['mediaclose'] = 'Tanca'; +$lang['js']['mediainsert'] = 'Inserta'; +$lang['js']['mediadisplayimg'] = 'Mostra la imatge'; +$lang['js']['mediadisplaylnk'] = 'Mostra només l\'enllaç'; +$lang['js']['mediasmall'] = 'Versió petita'; +$lang['js']['mediamedium'] = 'Versió mitjana'; +$lang['js']['medialarge'] = 'Versió gran'; +$lang['js']['mediaoriginal'] = 'Versió original'; +$lang['js']['medialnk'] = 'Enllaç a la pàgina de detalls'; +$lang['js']['mediadirect'] = 'Enllaç directe a l\'original'; +$lang['js']['medianolnk'] = 'No hi ha enllaç'; +$lang['js']['medianolink'] = 'No enllacis la imatge'; +$lang['js']['medialeft'] = 'Alinea la imatge a l\'esquerra.'; +$lang['js']['mediaright'] = 'Alinea la imatge a la dreta.'; +$lang['js']['mediacenter'] = 'Alinea la imatge al mig.'; +$lang['js']['medianoalign'] = 'No facis servir alineació.'; +$lang['js']['nosmblinks'] = 'Els enllaços amb recursos compartits de Windows només funcionen amb el Microsoft Internet Explorer. +Si voleu podeu copiar i enganxar l\'enllaç.'; +$lang['js']['linkwiz'] = 'Auxiliar d\'enllaços'; +$lang['js']['linkto'] = 'Enllaça a:'; +$lang['js']['del_confirm'] = 'Suprimiu aquesta entrada?'; +$lang['js']['restore_confirm'] = 'Vols realment restaurar aquesta versió?'; +$lang['js']['media_diff'] = 'Veure les diferències:'; +$lang['js']['media_diff_both'] = 'Un al costat de l\'altre'; +$lang['js']['media_diff_opacity'] = 'Resalta'; +$lang['js']['media_diff_portions'] = 'Llisca'; +$lang['js']['media_select'] = 'Escull els arxius'; +$lang['js']['media_upload_btn'] = 'Pujar'; +$lang['js']['media_done_btn'] = 'Fet'; +$lang['js']['media_drop'] = 'Arrossega aquí els arxius a pujar'; +$lang['js']['media_cancel'] = 'esborra'; +$lang['js']['media_overwrt'] = 'Sobreescriu els arxius existents'; $lang['rssfailed'] = 'S\'ha produït un error en recollir aquesta alimentació: '; $lang['nothingfound'] = 'No s\'ha trobat res.'; $lang['mediaselect'] = 'Selecció de fitxers'; @@ -108,14 +153,7 @@ $lang['deletefail'] = 'No s\'ha pogut suprimir el fitxer "%s". Compro $lang['mediainuse'] = 'No s\'ha pogut suprimir el fitxer "%s". Encara s\'està utilitzant.'; $lang['namespaces'] = 'Espais'; $lang['mediafiles'] = 'Fitxers disponibles en'; -$lang['js']['searchmedia'] = 'Cerca fitxers'; -$lang['js']['keepopen'] = 'Manté la finestra oberta'; -$lang['js']['hidedetails'] = 'Oculta detalls'; -$lang['js']['nosmblinks'] = 'Els enllaços amb recursos compartits de Windows només funcionen amb el Microsoft Internet Explorer. -Si voleu podeu copiar i enganxar l\'enllaç.'; -$lang['js']['linkwiz'] = 'Auxiliar d\'enllaços'; -$lang['js']['linkto'] = 'Enllaça a:'; -$lang['js']['del_confirm'] = 'Suprimiu aquesta entrada?'; +$lang['accessdenied'] = 'No teniu permís per a veure aquesta pàgina.'; $lang['mediausage'] = 'Utilitzeu la sintaxi següent per referir-vos a aquest enllaç:'; $lang['mediaview'] = 'Mostra el fitxer original'; $lang['mediaroot'] = 'arrel'; @@ -131,6 +169,10 @@ $lang['current'] = 'actual'; $lang['yours'] = 'La vostra versió'; $lang['diff'] = 'Mostra diferències amb la versió actual'; $lang['diff2'] = 'Mostra diferències entre les revisions seleccionades'; +$lang['difflink'] = 'Enllaç a la visualització de la comparació'; +$lang['diff_type'] = 'Veieu les diferències:'; +$lang['diff_inline'] = 'En línia'; +$lang['diff_side'] = 'Un al costat de l\'altre'; $lang['line'] = 'Línia'; $lang['breadcrumb'] = 'Camí'; $lang['youarehere'] = 'Sou aquí'; @@ -143,10 +185,20 @@ $lang['external_edit'] = 'edició externa'; $lang['summary'] = 'Resum d\'edició'; $lang['noflash'] = 'Per a visualitzar aquest contingut necessiteu el connector d\'Adobe Flash.'; $lang['download'] = 'Baixa el fragment'; +$lang['tools'] = 'Eines'; +$lang['user_tools'] = 'Eines de l\'usuari'; +$lang['site_tools'] = 'Eines del lloc'; +$lang['page_tools'] = 'Eines de la pàgina'; +$lang['skip_to_content'] = 'salta al contingut'; +$lang['sidebar'] = 'Barra lateral'; $lang['mail_newpage'] = 'pàgina afegida:'; $lang['mail_changed'] = 'pàgina modificada:'; $lang['mail_new_user'] = 'nou usuari:'; $lang['mail_upload'] = 'fitxer penjat:'; +$lang['changes_type'] = 'Veure els canvis de'; +$lang['pages_changes'] = 'Pàgines'; +$lang['media_changes'] = 'Arxius gràfics'; +$lang['both_changes'] = 'Pàgines i arxius gràfics'; $lang['qb_bold'] = 'Negreta'; $lang['qb_italic'] = 'Cursiva'; $lang['qb_underl'] = 'Subratllat'; @@ -187,13 +239,27 @@ $lang['img_copyr'] = 'Copyright'; $lang['img_format'] = 'Format'; $lang['img_camera'] = 'Càmera'; $lang['img_keywords'] = 'Paraules clau'; -$lang['subscribe_success'] = 'S\'ha afegit %s a la llista de subscripcions de %s'; -$lang['subscribe_error'] = 'S\'ha produït un error en afegir %s a la llista de subscripcions de %s'; -$lang['subscribe_noaddress'] = 'No hi ha cap adreça de correu associada al vostre nom d\'usuari. No se us ha pogut afegir a la llista de subscripcions.'; -$lang['unsubscribe_success'] = '%s ha estat suprimit de la llista de subscripcions de %s'; -$lang['unsubscribe_error'] = 'S\'ha produït un error en suprimir %s de la llista de subscripcions de %s'; +$lang['img_width'] = 'Ample'; +$lang['img_height'] = 'Alçada'; +$lang['subscr_subscribe_success'] = 'S\'ha afegit %s a la llista de subscripcions per %s'; +$lang['subscr_subscribe_error'] = 'Hi ha hagut un error a l\'afegir %s a la llista per %s'; +$lang['subscr_subscribe_noaddress'] = 'No hi ha cap adreça associada pel vostre nom d\'usuari, no podeu ser afegit a la llista de subscripcions'; +$lang['subscr_unsubscribe_success'] = 'S\'ha esborrat %s de la llista de subscripcions per %s'; +$lang['subscr_unsubscribe_error'] = 'Hi ha hagut un error a l\'esborrar %s de la llista de subscripcions per %s'; +$lang['subscr_already_subscribed'] = '%s ja està subscrit a %s'; +$lang['subscr_not_subscribed'] = '%s no està subscrit a %s'; +$lang['subscr_m_not_subscribed'] = 'En aquests moments no esteu subscrit a l\'actual pàgina o espai'; +$lang['subscr_m_new_header'] = 'Afegeix subcripció'; +$lang['subscr_m_current_header'] = 'Subscripcions actuals'; +$lang['subscr_m_unsubscribe'] = 'Donar-se de baixa'; +$lang['subscr_m_subscribe'] = 'Donar-se d\'alta'; +$lang['subscr_m_receive'] = 'Rebre'; +$lang['subscr_style_every'] = 'Envia\'m un correu electrònic per a cada canvi'; +$lang['subscr_style_digest'] = 'Envia\'m un correu electrònic amb un resum dels canvis per a cada pàgina (cada %.2f dies)'; +$lang['subscr_style_list'] = 'llistat de pàgines canviades des de l\'últim correu electrònic (cada %.2f dies)'; $lang['authmodfailed'] = 'La configuració de l\'autenticació d\'usuaris és errònia. Informeu els administradors del wiki.'; $lang['authtempfail'] = 'L\'autenticació d\'usuaris no està disponible temporalment. Si aquesta situació persisteix, si us plau informeu els administradors del wiki.'; +$lang['authpwdexpire'] = 'La vostra contrasenya caducarà en %d dies, l\'hauríeu de canviar aviat.'; $lang['i_chooselang'] = 'Trieu l\'idioma'; $lang['i_installer'] = 'Instal·lador de DokuWiki'; $lang['i_wikiname'] = 'Nom del wiki'; @@ -208,13 +274,14 @@ $lang['i_confexists'] = '%s ja existeix'; $lang['i_writeerr'] = 'No es pot crear %s. Comproveu els permisos del directori i/o del fitxer i creeu el fitxer manualment.'; $lang['i_badhash'] = 'dokuwiki.php no reconegut o modificat (hash=%s)'; $lang['i_badval'] = '%s - valor il·legal o buit'; -$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; -$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; +$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou DokuWiki.'; +$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou DokuWiki.'; $lang['i_policy'] = 'Política ACL inicial'; $lang['i_pol0'] = 'Wiki obert (tothom pot llegir, escriure i penjar fitxers)'; $lang['i_pol1'] = 'Wiki públic (tothom pot llegir, els usuaris registrats poden escriure i penjar fitxers)'; $lang['i_pol2'] = 'Wiki tancat (només els usuaris registrats poden llegir, escriure i penjar fitxers)'; $lang['i_retry'] = 'Reintenta'; +$lang['i_license'] = 'Escolliu el tipus de llicència que voleu fer servir per al vostre contingut:'; $lang['recent_global'] = 'Esteu veient els canvis recents de l\'espai %s. També podeu veure els canvis recents de tot el wiki.'; $lang['years'] = 'fa %d anys'; $lang['months'] = 'fa %d mesos'; @@ -223,3 +290,27 @@ $lang['days'] = 'fa %d dies'; $lang['hours'] = 'fa %d hores'; $lang['minutes'] = 'fa %d minuts'; $lang['seconds'] = 'fa %d segons'; +$lang['wordblock'] = 'El vostre canvi no s\'ha guardat perquè conté text blocat (spam)'; +$lang['media_uploadtab'] = 'Puja'; +$lang['media_searchtab'] = 'Busca'; +$lang['media_file'] = 'Fitxer'; +$lang['media_viewtab'] = 'Mostra'; +$lang['media_edittab'] = 'Edita'; +$lang['media_historytab'] = 'Històric'; +$lang['media_list_thumbs'] = 'Miniatura'; +$lang['media_list_rows'] = 'Files'; +$lang['media_sort_name'] = 'Nom'; +$lang['media_sort_date'] = 'Data'; +$lang['media_namespaces'] = 'Escolliu l\'espai'; +$lang['media_files'] = 'Arxius a %s'; +$lang['media_upload'] = 'Puja a %s'; +$lang['media_search'] = 'Busca a %s'; +$lang['media_view'] = '%s'; +$lang['media_viewold'] = '%s a %s'; +$lang['media_edit'] = 'Edita %s'; +$lang['media_history'] = 'Històric de %s'; +$lang['media_meta_edited'] = 'metadata editada'; +$lang['media_perm_read'] = 'No teniu permisos suficients per a llegir arxius.'; +$lang['media_perm_upload'] = 'No teniu permisos suficients per a pujar arxius'; +$lang['media_update'] = 'Puja la nova versió'; +$lang['media_restore'] = 'Restaura aquesta versió'; diff --git a/inc/lang/ca/mailwrap.html b/inc/lang/ca/mailwrap.html new file mode 100644 index 000000000..ed3bb6e9d --- /dev/null +++ b/inc/lang/ca/mailwrap.html @@ -0,0 +1,13 @@ + + +@TITLE@ + + + + +@HTMLBODY@ + +

    +Aquest correu electrònic ha estat generat per DokuWiki a @DOKUWIKIURL@. + + \ No newline at end of file diff --git a/inc/lang/ca/resetpwd.txt b/inc/lang/ca/resetpwd.txt new file mode 100644 index 000000000..565f1d5b0 --- /dev/null +++ b/inc/lang/ca/resetpwd.txt @@ -0,0 +1,3 @@ +===== Establiu una nova contrasenya ===== + +Introdueixi una nova contrasenya pel seu compte a aquest wiki. \ No newline at end of file diff --git a/inc/lang/ca/subscr_digest.txt b/inc/lang/ca/subscr_digest.txt new file mode 100644 index 000000000..2b95f97a5 --- /dev/null +++ b/inc/lang/ca/subscr_digest.txt @@ -0,0 +1,21 @@ +Hola! + +La pàgina @PAGE@ al wiki @TITLE@ ha canviat. +A continuació podeu veure els canvis: + +-------------------------------------------------------- +@DIFF@ +-------------------------------------------------------- + +Versió anterior: @OLDPAGE@ +Nova versió: @NEWPAGE@ + +Si voleu cancel·lar les notificacions per a la pàgina, accediu al wiki a +@DOKUWIKIURL@, visiteu +@SUBSCRIBE@ +i doneu-vos de baixa dels canvis de la pàgina o de l'espai. + + +-- +Aquest mail ha estat generat per DokuWiki a +@DOKUWIKIURL@ \ No newline at end of file diff --git a/inc/lang/ca/subscr_form.txt b/inc/lang/ca/subscr_form.txt new file mode 100644 index 000000000..d3679454f --- /dev/null +++ b/inc/lang/ca/subscr_form.txt @@ -0,0 +1,3 @@ +===== Gestió de les Subscripcions ===== + +Aquesta pàgina podeu gestiona les vostres subscripcions per a les pàgines i els espais actuals. \ No newline at end of file diff --git a/inc/lang/ca/subscr_list.txt b/inc/lang/ca/subscr_list.txt new file mode 100644 index 000000000..56bcad545 --- /dev/null +++ b/inc/lang/ca/subscr_list.txt @@ -0,0 +1,21 @@ +Hola! + +Alguna(es) pàgina(es) de l'espai @PAGE@ al wiki @TITLE@ han canviat. +A continuació podeu veure els canvis: + +-------------------------------------------------------- +@DIFF@ +-------------------------------------------------------- + +Versió anterior: @OLDPAGE@ +Nova versió: @NEWPAGE@ + +Si voleu cancel·lar les notificacions per a la pàgina, accediu al wiki a +@DOKUWIKIURL@, visiteu +@SUBSCRIBE@ +i doneu-vos de baixa dels canvis de la pàgina o de l'espai. + + +-- +Aquest mail ha estat generat per DokuWiki a +@DOKUWIKIURL@ \ No newline at end of file -- cgit v1.2.3 From e6c4a8221e93692572a1e78b9319a94b5ba07a0c Mon Sep 17 00:00:00 2001 From: Erdem Date: Fri, 18 Jan 2013 10:08:48 +0100 Subject: Turkish language update --- inc/lang/tr/lang.php | 55 +++++++++++++++++++++++++++++++++++++++++++++--- inc/lang/tr/resetpwd.txt | 3 +++ 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 inc/lang/tr/resetpwd.txt (limited to 'inc') diff --git a/inc/lang/tr/lang.php b/inc/lang/tr/lang.php index a8d8c5ac9..5430905b1 100644 --- a/inc/lang/tr/lang.php +++ b/inc/lang/tr/lang.php @@ -8,6 +8,7 @@ * @author Cihan Kahveci * @author Yavuz Selim * @author Caleb Maclennan + * @author farukerdemoncel@gmail.com */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -43,11 +44,14 @@ $lang['btn_backtomedia'] = 'Çokluortam dosyası seçimine dön'; $lang['btn_subscribe'] = 'Sayfa Değişikliklerini Bildir'; $lang['btn_profile'] = 'Kullanıcı Bilgilerini Güncelle'; $lang['btn_reset'] = 'Sıfırla'; +$lang['btn_resendpwd'] = 'Yeni şifre belirle'; $lang['btn_draft'] = 'Taslağı düzenle'; $lang['btn_recover'] = 'Taslağı geri yükle'; $lang['btn_draftdel'] = 'Taslağı sil'; $lang['btn_revert'] = 'Geri Yükle'; $lang['btn_register'] = 'Kayıt ol'; +$lang['btn_apply'] = 'Uygula'; +$lang['btn_media'] = 'Çokluortam Yöneticisi'; $lang['loggedinas'] = 'Giriş ismi'; $lang['user'] = 'Kullanıcı ismi'; $lang['pass'] = 'Parola'; @@ -77,6 +81,7 @@ $lang['profnoempty'] = 'Boş isim veya e-posta adresine izin verilmiyo $lang['profchanged'] = 'Kullanıcı bilgileri başarıyla değiştirildi.'; $lang['pwdforget'] = 'Parolanızı mı unuttunuz? Yeni bir parola alın'; $lang['resendna'] = 'Bu wiki parolayı tekrar göndermeyi desteklememektedir.'; +$lang['resendpwd'] = 'İçin yeni şifre belirle'; $lang['resendpwdmissing'] = 'Üzgünüz, tüm alanları doldurmalısınız.'; $lang['resendpwdnouser'] = 'Üzgünüz, veritabanımızda bu kullanıcıyı bulamadık.'; $lang['resendpwdbadauth'] = 'Üzgünüz, bu doğrulama kodu doğru değil. Doğrulama linkini tam olarak kullandığınıza emin olun.'; @@ -91,7 +96,7 @@ $lang['txt_filename'] = 'Dosya adı (zorunlu değil)'; $lang['txt_overwrt'] = 'Mevcut dosyanın üstüne yaz'; $lang['lockedby'] = 'Şu an şunun tarafından kilitli:'; $lang['lockexpire'] = 'Kilitin açılma tarihi:'; -$lang['js']['willexpire'] = 'Bu sayfayı değiştirme kilidinin süresi yaklaşık bir dakika içinde geçecek.\nÇakışmaları önlemek için önizleme tuşunu kullanarak kilit sayacını sıfırla.'; +$lang['js']['willexpire'] = 'Bu sayfayı değiştirme kilidinin süresi yaklaşık bir dakika içinde geçecek.\nÇakışmaları önlemek için önizleme tuşunu kullanarak kilit sayacını sıfırla.'; $lang['js']['notsavedyet'] = 'Kaydedilmemiş değişiklikler kaybolacak. Devam etmek istiyor musunuz?'; $lang['js']['searchmedia'] = 'Dosyalar için Ara'; @@ -122,6 +127,14 @@ $lang['js']['nosmblinks'] = 'Windows paylaşımı sadece Microsoft Internet $lang['js']['linkwiz'] = 'Bağlantı sihirbazı'; $lang['js']['linkto'] = 'Bağlantı:'; $lang['js']['del_confirm'] = 'Bu girişi sil?'; +$lang['js']['restore_confirm'] = 'Bu sürüme geri dönmek istediğinizden emin misiniz?'; +$lang['js']['media_diff'] = 'Farkları gör:'; +$lang['js']['media_select'] = 'Dosyalar seç...'; +$lang['js']['media_upload_btn'] = 'Yükle'; +$lang['js']['media_done_btn'] = 'Bitti'; +$lang['js']['media_drop'] = 'Yüklemek istediğiniz dosyaları buraya bırakın'; +$lang['js']['media_cancel'] = 'kaldır'; +$lang['js']['media_overwrt'] = 'Var olan dosyaların üzerine yaz'; $lang['rssfailed'] = 'Bu beslemeyi çekerken hata oluştu: '; $lang['nothingfound'] = 'Hiçbir şey yok.'; $lang['mediaselect'] = 'Çokluortam dosyası seçimi'; @@ -172,6 +185,9 @@ $lang['mail_newpage'] = 'sayfa eklenme:'; $lang['mail_changed'] = 'sayfa değiştirilme:'; $lang['mail_new_user'] = 'yeni kullanıcı'; $lang['mail_upload'] = 'dosya yüklendi:'; +$lang['pages_changes'] = 'Sayfalar'; +$lang['media_changes'] = 'Çokluortam dosyaları'; +$lang['both_changes'] = 'Sayfalar ve çoklu ortam dosyaları'; $lang['qb_bold'] = 'Kalın Yazı'; $lang['qb_italic'] = 'Eğik Yazı'; $lang['qb_underl'] = 'Altı Çizgili Yazı'; @@ -209,8 +225,14 @@ $lang['img_copyr'] = 'Telif Hakkı'; $lang['img_format'] = 'Biçim'; $lang['img_camera'] = 'Fotoğraf Makinası'; $lang['img_keywords'] = 'Anahtar Sözcükler'; +$lang['img_width'] = 'Genişlik'; +$lang['img_height'] = 'Yükseklik'; +$lang['img_manager'] = 'Ortam oynatıcısında göster'; +$lang['subscr_m_subscribe'] = 'Kayıt ol'; +$lang['subscr_m_receive'] = 'Al'; $lang['authmodfailed'] = 'Yanlış kullanıcı onaylama ayarı. Lütfen Wiki yöneticisine bildiriniz.'; $lang['authtempfail'] = 'Kullanıcı doğrulama geçici olarak yapılamıyor. Eğer bu durum devam ederse lütfen Wiki yöneticine haber veriniz.'; +$lang['authpwdexpire'] = 'Şifreniz %d gün sonra geçersiz hale gelecek, yakın bir zamanda değiştirmelisiniz.'; $lang['i_chooselang'] = 'Dili seçiniz'; $lang['i_installer'] = 'Dokuwiki Kurulum Sihirbazı'; $lang['i_wikiname'] = 'Wiki Adı'; @@ -225,11 +247,38 @@ $lang['i_confexists'] = '%s zaten var'; $lang['i_writeerr'] = '%s oluşturulamadı. Dosya/Klasör izin ayarlarını gözden geçirip dosyayı elle oluşturmalısınız.'; $lang['i_badhash'] = 'dokuwiki.php tanınamadı ya da değiştirilmiş (hash=%s)'; $lang['i_badval'] = '%s - Yanlış veya boş değer'; -$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; -$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; +$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. Yeni DokuWikinizi kullanabilirsiniz.'; +$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. Yeni DokuWikinizi kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.'; $lang['i_policy'] = 'İlk ACL ayarı'; $lang['i_pol0'] = 'Tamamen Açık Wiki (herkes okuyabilir, yazabilir ve dosya yükleyebilir)'; $lang['i_pol1'] = 'Açık Wiki (herkes okuyabilir, ancak sadece üye olanlar yazabilir ve dosya yükleyebilir)'; $lang['i_pol2'] = 'Kapalı Wiki (sadece üye olanlar okuyabilir, yazabilir ve dosya yükleyebilir)'; $lang['i_retry'] = 'Tekrar Dene'; +$lang['i_license'] = 'Lütfen içeriği hangi lisans altında yayınlamak istediğniizi belirtin:'; $lang['recent_global'] = '%s namespace\'i içerisinde yapılan değişiklikleri görüntülemektesiniz. Wiki\'deki tüm değişiklikleri de bu adresten görebilirsiniz. '; +$lang['years'] = '%d yıl önce'; +$lang['months'] = '%d ay önce'; +$lang['weeks'] = '%d hafta önce'; +$lang['days'] = '%d gün önce'; +$lang['hours'] = '%d saat önce'; +$lang['minutes'] = '%d dakika önce'; +$lang['seconds'] = '%d saniye önce'; +$lang['wordblock'] = 'Değişikliğiniz kaydedilmedi çünkü istenmeyen mesaj içeriyor (spam).'; +$lang['media_uploadtab'] = 'Karşıya yükle'; +$lang['media_searchtab'] = 'Ara'; +$lang['media_file'] = 'Dosya'; +$lang['media_viewtab'] = 'Görünüm'; +$lang['media_edittab'] = 'Düzenle'; +$lang['media_historytab'] = 'Geçmiş'; +$lang['media_list_thumbs'] = 'Küçük resimler'; +$lang['media_list_rows'] = 'Satırlar'; +$lang['media_sort_name'] = 'İsim'; +$lang['media_sort_date'] = 'Tarih'; +$lang['media_files'] = '%s deki dosyalar'; +$lang['media_upload'] = '%s dizinine yükle'; +$lang['media_search'] = '%s dizininde ara'; +$lang['media_view'] = '%s'; +$lang['media_edit'] = 'Düzenle %s'; +$lang['media_history'] = 'Geçmiş %s'; +$lang['media_perm_upload'] = 'Üzgünüm, karşıya dosya yükleme yetkiniz yok.'; +$lang['media_restore'] = 'Bu sürümü eski haline getir'; diff --git a/inc/lang/tr/resetpwd.txt b/inc/lang/tr/resetpwd.txt new file mode 100644 index 000000000..1ed758693 --- /dev/null +++ b/inc/lang/tr/resetpwd.txt @@ -0,0 +1,3 @@ + ====== Yeni şifre belirle ====== + +Lütfen bu wiki hesabınız için yeni bir şifre belirleyin. \ No newline at end of file -- cgit v1.2.3 From 16c665d9fa1d3d1c5d82866a942ba8cb6db3a4ee Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 10:41:26 +0100 Subject: removed data parameter in subscription_handle_post() now the data parameter, which really is a timestamp is only handled internally of the subscription class --- inc/actions.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/actions.php b/inc/actions.php index 911edf936..4083b0454 100644 --- a/inc/actions.php +++ b/inc/actions.php @@ -784,8 +784,7 @@ function subscription_handle_post(&$params) { $style = null; } - $data = in_array($style, array('list', 'digest')) ? time() : null; - $params = compact('target', 'style', 'data', 'action'); + $params = compact('target', 'style', 'action'); } //Setup VIM: ex: et ts=2 : -- cgit v1.2.3 From cfbb9916556595cd8987f6c79771883dde656185 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 10:47:21 +0100 Subject: removed unused vars --- inc/common.php | 3 --- 1 file changed, 3 deletions(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index 20e0af389..bc49e76b2 100644 --- a/inc/common.php +++ b/inc/common.php @@ -1105,10 +1105,7 @@ function saveOldRevision($id) { * @author Andreas Gohr */ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = array()) { - global $lang; global $conf; - global $INFO; - global $DIFF_INLINESTYLES; // decide if there is something to do, eg. whom to mail if($who == 'admin') { -- cgit v1.2.3 From edf986ad6271abac1f00055d3de4a9857cae1553 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 10:49:41 +0100 Subject: comment adjusted --- inc/subscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 44248a39d..92c1e8579 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -381,7 +381,7 @@ class Subscription { * Send the diff for some page change * * @param string $subscriber_mail The target mail address - * @param string $template Mail template, should be 'subscr_digest' or 'subscr_single' + * @param string $template Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...) * @param string $id Page for which the notification is * @param int|null $rev Old revision if any * @param string $summary Change summary if any -- cgit v1.2.3 From 004e7e6efb2e8da1d76f3959667ca0dbf8b0aaf6 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 10:52:26 +0100 Subject: simplified subscription->add() code a bit --- inc/subscription.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 92c1e8579..e888ee27f 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -204,20 +204,16 @@ class Subscription { // delete any existing subscription $this->remove($id, $user); - $user = auth_nameencode(trim($user)); + $user = auth_nameencode(trim($user)); $style = trim($style); - $data = trim($data); + $data = trim($data); if(!$user) throw new Exception('no subscription user given'); if(!$style) throw new Exception('no subscription style given'); if(!$data) $data = time(); //always add current time for new subscriptions - $line = "$user $style"; - if($data) $line .= " $data"; - $line .= "\n"; - + $line = "$user $style $data\n"; $file = $this->file($id); - return io_saveFile($file, $line, true); } -- cgit v1.2.3 From 8b87bc02a80bd1436f031d46f2d1e167b815323a Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 11:01:04 +0100 Subject: fixed lists in HTML mails --- inc/subscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index e888ee27f..fc26e42e8 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -503,7 +503,7 @@ class Subscription { $tlist .= '* '.$link.NL; $hlist .= '
  • '.hsc($id).'
  • '.NL; } - $hlist = '
'; + $hlist .= ''; $id = prettyprint_id($ns_id); $trep = array( -- cgit v1.2.3 From 903e04c3aaaad8e237d8e50434aed40692672a43 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 11:07:46 +0100 Subject: correctly escape diffs in HTML mails --- inc/subscription.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index fc26e42e8..e4cabffed 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -398,17 +398,22 @@ class Subscription { if($rev) { $subject = 'changed'; $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&'); - $df = new Diff(explode("\n", rawWiki($id, $rev)), - explode("\n", rawWiki($id))); + + $old_content = rawWiki($id, $rev); + $new_content = rawWiki($id); + + $df = new Diff(explode("\n", $old_content), + explode("\n", $new_content)); $dformat = new UnifiedDiffFormatter(); $tdiff = $dformat->format($df); $DIFF_INLINESTYLES = true; + $df = new Diff(explode("\n", hsc($old_content)), + explode("\n", hsc($new_content))); $dformat = new InlineDiffFormatter(); $hdiff = $dformat->format($df); $hdiff = ''.$hdiff.'
'; $DIFF_INLINESTYLES = false; - } else { $subject = 'newpage'; $trep['OLDPAGE'] = '---'; -- cgit v1.2.3 From 29c964eb1ccaac4204b31ac7409bd91e93e9ed0c Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 18 Jan 2013 11:16:53 +0100 Subject: readded mailfromnobody to subscription sending this was lost because the subscription branch, branched before this change in master --- inc/subscription.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index e4cabffed..dfb3533fd 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -546,6 +546,7 @@ class Subscription { */ protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null) { global $lang; + global $conf; $text = rawLocale($template); $subject = $lang['mail_'.$subject].' '.$context; @@ -553,6 +554,7 @@ class Subscription { $mail->bcc($subscriber_mail); $mail->subject($subject); $mail->setBody($text, $trep, $hrep); + $mail->from($conf['mailfromnobody']); if(isset($trep['SUBSCRIBE'])) { $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); } -- cgit v1.2.3 From 77e5adb1a4bff9e9dd43f7c10beaa235c6eb5398 Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Sat, 19 Jan 2013 12:18:36 +0100 Subject: wiki.getAttachments returns 'perms' according to documentation --- inc/RemoteAPICore.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'inc') diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php index c04a14f5c..72af65329 100644 --- a/inc/RemoteAPICore.php +++ b/inc/RemoteAPICore.php @@ -344,6 +344,8 @@ class RemoteAPICore { for($i=0; $i<$len; $i++) { unset($data[$i]['meta']); + $data[$i]['perms'] = $data[$i]['perm']; + unset($data[$i]['perm']); $data[$i]['lastModified'] = $this->api->toDate($data[$i]['mtime']); } return $data; -- cgit v1.2.3 From 5eb9e8678ddc58a01929a9f340a01048836b47d3 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 19 Jan 2013 16:59:39 +0100 Subject: Indexer: Added page and meta value rename functions With these functions that search index can be updated after page moves or mass metadata updates without the need to reindex the whole page/wiki. These functions will be used by the new pagemove plugin. --- inc/indexer.php | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index 7a62345bf..70eac035b 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -338,6 +338,106 @@ class Doku_Indexer { return true; } + /** + * Rename a page in the search index without changing the indexed content + * + * @param string $oldpage The old page name + * @param string $newpage The new page name + * @return string|bool If the page was successfully renamed, can be a message in the case of an error + */ + public function renamePage($oldpage, $newpage) { + if (!$this->lock()) return 'locked'; + + $pages = $this->getPages(); + + $id = array_search($oldpage, $pages); + if ($id === false) { + $this->unlock(); + return 'page is not in index'; + } + + $new_id = array_search($newpage, $pages); + if ($new_id !== false) { + $this->unlock(); + // make sure the page is not in the index anymore + $this->deletePage($newpage); + if (!$this->lock()) return 'locked'; + + $pages[$new_id] = 'deleted:'.time().rand(0, 9999); + } + + $pages[$id] = $newpage; + + // update index + if (!$this->saveIndex('page', '', $pages)) { + $this->unlock(); + return false; + } + + // reset the pid cache + $this->pidCache = array(); + + $this->unlock(); + return true; + } + + /** + * Renames a meta value in the index. This doesn't change the meta value in the pages, it assumes that all pages + * will be updated. + * + * @param string $key The metadata key of which a value shall be changed + * @param string $oldvalue The old value that shall be renamed + * @param string $newvalue The new value to which the old value shall be renamed, can exist (then values will be merged) + * @return bool|string If renaming the value has been successful, false or error message on error. + */ + public function renameMetaValue($key, $oldvalue, $newvalue) { + if (!$this->lock()) return 'locked'; + + // change the relation references index + $metavalues = $this->getIndex($key, '_w'); + $oldid = array_search($oldvalue, $metavalues); + if ($oldid !== false) { + $newid = array_search($newvalue, $metavalues); + if ($newid !== false) { + // free memory + unset ($metavalues); + + // okay, now we have two entries for the same value. we need to merge them. + $indexline = $this->getIndexKey($key, '_i', $oldid); + if ($indexline != '') { + $newindexline = $this->getIndexKey($key, '_i', $newid); + $pagekeys = $this->getIndex($key, '_p'); + $parts = explode(':', $indexline); + foreach ($parts as $part) { + list($id, $count) = explode('*', $part); + $newindexline = $this->updateTuple($newindexline, $id, $count); + + $keyline = explode(':', $pagekeys[$id]); + // remove old meta value + $keyline = array_diff($keyline, array($oldid)); + // add new meta value when not already present + if (!in_array($newid, $keyline)) { + array_push($keyline, $newid); + } + $pagekeys[$id] = implode(':', $keyline); + } + $this->saveIndex($key, '_p', $pagekeys); + unset($pagekeys); + $this->saveIndexKey($key, '_i', $oldid, ''); + $this->saveIndexKey($key, '_i', $newid, $newindexline); + } + } else { + $metavalues[$oldid] = $newvalue; + if (!$this->saveIndex($key, '_w', $metavalues)) { + $this->unlock(); + return false; + } + } + } + + $this->unlock(); + return true; + } /** * Remove a page from the index * -- cgit v1.2.3 From 804f11b20e4a4f72cc45900e46f074fbd39b4649 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 19 Jan 2013 17:06:34 +0100 Subject: Correct PHPDoc comment of DokuWiki_Plugin::locale_xhtml() --- inc/plugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/plugin.php b/inc/plugin.php index e4bba989d..153e89407 100644 --- a/inc/plugin.php +++ b/inc/plugin.php @@ -81,8 +81,8 @@ class DokuWiki_Plugin { * retrieve a language dependent file and pass to xhtml renderer for display * plugin equivalent of p_locale_xhtml() * - * @param $id id of language dependent wiki page - * @return string parsed contents of the wiki page in xhtml format + * @param string $id id of language dependent wiki page + * @return string parsed contents of the wiki page in xhtml format */ function locale_xhtml($id) { return p_cached_output($this->localFN($id)); -- cgit v1.2.3 From 45c1b04dfbc6014555d58bc561e6c277a2c2dc79 Mon Sep 17 00:00:00 2001 From: Guillaume Turri Date: Mon, 21 Jan 2013 09:46:12 +0100 Subject: Increment DOKU_API_VERSION Because wiki.getAttachments now returns 'perms' instead of 'perm' --- inc/RemoteAPICore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php index 72af65329..07e6aa090 100644 --- a/inc/RemoteAPICore.php +++ b/inc/RemoteAPICore.php @@ -3,7 +3,7 @@ /** * Increased whenever the API is changed */ -define('DOKU_API_VERSION', 7); +define('DOKU_API_VERSION', 8); class RemoteAPICore { -- cgit v1.2.3 From f99ea547962bcbfb89921395f8aa82be59699303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 23 Jan 2013 13:45:47 +0200 Subject: fixes from bee9f377bc547c99fe99b4e38199cb92cf668554 commit notes --- inc/Tar.class.php | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php index 59e14c705..20f397395 100644 --- a/inc/Tar.class.php +++ b/inc/Tar.class.php @@ -17,7 +17,7 @@ * * $tar = new Tar(); * $tar->open('myfile.tgz'); - * $tar->extract(/tmp); + * $tar->extract('/tmp'); * * To create a new TAR archive directly on the filesystem (low memory requirements), create() it, * add*() files and close() it: @@ -81,7 +81,7 @@ class Tar { $this->fh = @fopen($this->file, 'rb'); } - if(!$this->fh) throw(new TarIOException('Could not open file for reading: '.$this->file)); + if(!$this->fh) throw new TarIOException('Could not open file for reading: '.$this->file); $this->closed = false; } @@ -107,9 +107,9 @@ class Tar { * Reopen the file with open() again if you want to do additional operations */ public function contents() { - if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive')); + if($this->closed || !$this->file) throw new TarIOException('Can not read from a closed archive'); - $result = Array(); + $result = array(); while($read = $this->readbytes(512)) { $header = $this->parseHeader($read); if(!is_array($header)) continue; @@ -148,7 +148,7 @@ class Tar { * @return array */ function extract($outdir, $strip = '', $exclude = '', $include = '') { - if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive')); + if($this->closed || !$this->file) throw new TarIOException('Can not read from a closed archive'); $outdir = rtrim($outdir, '/'); io_mkdir_p($outdir); @@ -207,7 +207,7 @@ class Tar { // is this a file? if(!$header['typeflag']) { $fp = fopen($output, "wb"); - if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output)); + if(!$fp) throw new TarIOException('Could not open file for writing: '.$output); $size = floor($header['size'] / 512); for($i = 0; $i < $size; $i++) { @@ -260,7 +260,7 @@ class Tar { $this->fh = @fopen($this->file, 'wb'); } - if(!$this->fh) throw(new TarIOException('Could not open file for writing: '.$this->file)); + if(!$this->fh) throw new TarIOException('Could not open file for writing: '.$this->file); } $this->writeaccess = false; $this->closed = false; @@ -275,13 +275,13 @@ class Tar { * @throws TarIOException */ public function addFile($file, $name = '') { - if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added')); + if($this->closed) throw new TarIOException('Archive has been closed, files can no longer be added'); if(!$name) $name = $file; $name = $this->cleanPath($name); $fp = fopen($file, 'rb'); - if(!$fp) throw(new TarIOException('Could not open file for reading: '.$file)); + if(!$fp) throw new TarIOException('Could not open file for reading: '.$file); // create file header and copy all stat info from the original file clearstatcache(false, $file); @@ -314,7 +314,7 @@ class Tar { * @throws TarIOException */ public function addData($name, $data, $uid = 0, $gid = 0, $perm = 0666, $mtime = 0) { - if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added')); + if($this->closed) throw new TarIOException('Archive has been closed, files can no longer be added'); $name = $this->cleanPath($name); $len = strlen($data); @@ -401,7 +401,7 @@ class Tar { if($comptype === Tar::COMPRESS_AUTO) $comptype = $this->filetype($file); if(!file_put_contents($file, $this->getArchive($comptype, $complevel))) { - throw(new TarIOException('Could not write to file: '.$file)); + throw new TarIOException('Could not write to file: '.$file); } } @@ -439,14 +439,14 @@ class Tar { } else { $written = @fwrite($this->fh, $data); } - if($written === false) throw(new TarIOException('Failed to write to archive stream')); + if($written === false) throw new TarIOException('Failed to write to archive stream'); return $written; } /** * Skip forward in the open file pointer * - * This is basically a wrapper around seek() (and a workarounf for bzip2) + * This is basically a wrapper around seek() (and a workaround for bzip2) * * @param int $bytes seek to this position */ @@ -494,11 +494,11 @@ class Tar { } // values are needed in octal - $uid = sprintf("%6s ", DecOct($uid)); - $gid = sprintf("%6s ", DecOct($gid)); - $perm = sprintf("%6s ", DecOct($perm)); - $size = sprintf("%11s ", DecOct($size)); - $mtime = sprintf("%11s", DecOct($mtime)); + $uid = sprintf("%6s ", decoct($uid)); + $gid = sprintf("%6s ", decoct($gid)); + $perm = sprintf("%6s ", decoct($perm)); + $size = sprintf("%11s ", decoct($size)); + $mtime = sprintf("%11s", decoct($mtime)); $data_first = pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime); $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, ""); @@ -511,7 +511,7 @@ class Tar { $this->writebytes($data_first); - $chks = pack("a8", sprintf("%6s ", DecOct($chks))); + $chks = pack("a8", sprintf("%6s ", decoct($chks))); $this->writebytes($chks.$data_last); } @@ -598,11 +598,11 @@ class Tar { */ protected function compressioncheck($comptype) { if($comptype === Tar::COMPRESS_GZIP && !function_exists('gzopen')) { - throw(new TarIllegalCompressionException('No gzip support available')); + throw new TarIllegalCompressionException('No gzip support available'); } if($comptype === Tar::COMPRESS_BZIP && !function_exists('bzopen')) { - throw(new TarIllegalCompressionException('No bzip2 support available')); + throw new TarIllegalCompressionException('No bzip2 support available'); } } @@ -631,4 +631,4 @@ class TarIOException extends Exception { } class TarIllegalCompressionException extends Exception { -} \ No newline at end of file +} -- cgit v1.2.3 From f2cb3ec76dec3fe2b40f25765ef842223c7132fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 23 Jan 2013 13:55:25 +0200 Subject: handle bzip1 as well in fact .tbz is tar.bz (bzip1) and .tbz2 is what tar.bz2 is used commonly. --- inc/Tar.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php index 59e14c705..86fe65abb 100644 --- a/inc/Tar.class.php +++ b/inc/Tar.class.php @@ -618,7 +618,7 @@ class Tar { $file = strtolower($file); if(substr($file, -3) == '.gz' || substr($file, -4) == '.tgz') { $comptype = Tar::COMPRESS_GZIP; - } elseif(substr($file, -4) == '.bz2' || substr($file, -4) == '.tbz') { + } elseif(substr($file, -4) == '.bz2' || substr($file, -3) == '.bz' || substr($file, -4) == '.tbz' || substr($file, -5) == ".tbz2") { $comptype = Tar::COMPRESS_BZIP; } else { $comptype = Tar::COMPRESS_NONE; @@ -631,4 +631,4 @@ class TarIOException extends Exception { } class TarIllegalCompressionException extends Exception { -} \ No newline at end of file +} -- cgit v1.2.3 From 7afccd0aab62eed3797273e5241b7729bab1fb3f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 23 Jan 2013 16:16:18 +0100 Subject: Revert "handle bzip1 as well" This reverts commit f2cb3ec76dec3fe2b40f25765ef842223c7132fe. Turns out I was too fast merging this. I can't get PHP's bzip handler to handle a bzip1 compressed file. --- inc/Tar.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/Tar.class.php b/inc/Tar.class.php index 20d892e68..20f397395 100644 --- a/inc/Tar.class.php +++ b/inc/Tar.class.php @@ -618,7 +618,7 @@ class Tar { $file = strtolower($file); if(substr($file, -3) == '.gz' || substr($file, -4) == '.tgz') { $comptype = Tar::COMPRESS_GZIP; - } elseif(substr($file, -4) == '.bz2' || substr($file, -3) == '.bz' || substr($file, -4) == '.tbz' || substr($file, -5) == ".tbz2") { + } elseif(substr($file, -4) == '.bz2' || substr($file, -4) == '.tbz') { $comptype = Tar::COMPRESS_BZIP; } else { $comptype = Tar::COMPRESS_NONE; -- cgit v1.2.3 From 182ac905691e45146536796b39ef9f1420fb79b5 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 24 Jan 2013 15:17:01 +0100 Subject: fix custom editor loading Custom editors can also be loaded when not a section is given. For example this happens on a preview click where the page is already separated into pre, suf and text. --- inc/html.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/html.php b/inc/html.php index f4e6af663..5c1c75cf6 100644 --- a/inc/html.php +++ b/inc/html.php @@ -1390,8 +1390,7 @@ function html_edit(){ $data = array('form' => $form, 'wr' => $wr, 'media_manager' => true, - 'target' => ($INPUT->has('target') && $wr && - $RANGE !== '') ? $INPUT->str('target') : 'section', + 'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section', 'intro_locale' => $include); if ($data['target'] !== 'section') { -- cgit v1.2.3 From 368571d88a6155195d8d285c5facd569e8a5b385 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 25 Jan 2013 10:58:01 +0100 Subject: added missing context for list mails --- inc/subscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index dfb3533fd..6d62e4ced 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -523,7 +523,7 @@ class Subscription { return $this->send( $subscriber_mail, 'subscribe_list', - + $ns_id, 'subscr_list', $trep, $hrep ); } -- cgit v1.2.3 From 6cd7e49ba5bd41e3345b5cc4418f1e71476b3991 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 25 Jan 2013 10:58:15 +0100 Subject: only use mailfromnobody for bulk mails --- inc/subscription.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 6d62e4ced..62cfd1509 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -554,7 +554,9 @@ class Subscription { $mail->bcc($subscriber_mail); $mail->subject($subject); $mail->setBody($text, $trep, $hrep); - $mail->from($conf['mailfromnobody']); + if(in_array($template, array('subscr_list', 'subscr_digest'))){ + $mail->from($conf['mailfromnobody']); + } if(isset($trep['SUBSCRIBE'])) { $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); } -- cgit v1.2.3 From c38b7fab9eeb9456aaf5fe8e4481c3b8569e5644 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 26 Jan 2013 11:17:01 +0100 Subject: link directly to subscription management in mails This was updated in the english translation a while ago, but was still missing in some translations --- inc/lang/ar/subscr_single.txt | 2 +- inc/lang/bg/subscr_single.txt | 2 +- inc/lang/da/subscr_single.txt | 2 +- inc/lang/de-informal/subscr_single.txt | 2 +- inc/lang/de/subscr_single.txt | 2 +- inc/lang/en/subscr_single.txt | 2 +- inc/lang/eo/subscr_single.txt | 2 +- inc/lang/he/subscr_single.txt | 2 +- inc/lang/ia/subscr_single.txt | 2 +- inc/lang/it/subscr_single.txt | 2 +- inc/lang/ko/subscr_single.txt | 2 +- inc/lang/la/subscr_digest.txt | 2 +- inc/lang/la/subscr_single.txt | 2 +- inc/lang/no/subscr_digest.txt | 2 +- inc/lang/no/subscr_list.txt | 2 +- inc/lang/no/subscr_single.txt | 2 +- inc/lang/pt/subscr_single.txt | 2 +- inc/lang/sq/subscr_single.txt | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) (limited to 'inc') diff --git a/inc/lang/ar/subscr_single.txt b/inc/lang/ar/subscr_single.txt index 5c62aeaeb..611688415 100644 --- a/inc/lang/ar/subscr_single.txt +++ b/inc/lang/ar/subscr_single.txt @@ -15,7 +15,7 @@ لإلغاء إشعارات الصفحة,لُج الويكي في @DOKUWIKIURL@ ثم زُر -@NEWPAGE@ +@SUBSCRIBE@ وألغ الاشتراك من تغييرات الصفحة و/أو النطاق. -- diff --git a/inc/lang/bg/subscr_single.txt b/inc/lang/bg/subscr_single.txt index 7b26f8e96..a74a21fb8 100644 --- a/inc/lang/bg/subscr_single.txt +++ b/inc/lang/bg/subscr_single.txt @@ -14,7 +14,7 @@ Нова версия: @NEWPAGE@ Ако желаете да прекратите уведомяването за страницата трябва да се впишете на адрес @DOKUWIKIURL@, да посетите -@NEWPAGE@ +@SUBSCRIBE@ и да прекратите абонамента за промени по страницата или именното пространство. -- diff --git a/inc/lang/da/subscr_single.txt b/inc/lang/da/subscr_single.txt index 64b14588c..cdc554513 100644 --- a/inc/lang/da/subscr_single.txt +++ b/inc/lang/da/subscr_single.txt @@ -15,7 +15,7 @@ Ny Revision: @NEWPAGE@ For at slå side notifikationer fra, skal du logge ind på @DOKUWIKIURL@ og besøge -@NEWPAGE@ +@SUBSCRIBE@ og slå abonnoment for side / navnerum ændringer fra. -- diff --git a/inc/lang/de-informal/subscr_single.txt b/inc/lang/de-informal/subscr_single.txt index 3c557bc17..6e3f58b9f 100644 --- a/inc/lang/de-informal/subscr_single.txt +++ b/inc/lang/de-informal/subscr_single.txt @@ -15,7 +15,7 @@ Neue Revision: @NEWPAGE@ Um das Abonnement für diese Seite aufzulösen, melde dich im Wiki an @DOKUWIKIURL@, besuche dann -@NEWPAGE@ +@SUBSCRIBE@ und klicke auf den Link 'Aboverwaltung'. -- diff --git a/inc/lang/de/subscr_single.txt b/inc/lang/de/subscr_single.txt index f3e1cd393..da9914e50 100644 --- a/inc/lang/de/subscr_single.txt +++ b/inc/lang/de/subscr_single.txt @@ -15,7 +15,7 @@ Neue Revision: @NEWPAGE@ Um das Abonnement für diese Seite aufzulösen, melden Sie sich im Wiki an @DOKUWIKIURL@, besuchen dann -@NEWPAGE@ +@SUBSCRIBE@ und klicken auf die Taste 'Aboverwaltung'. -- diff --git a/inc/lang/en/subscr_single.txt b/inc/lang/en/subscr_single.txt index 673c4c32a..0bc310e04 100644 --- a/inc/lang/en/subscr_single.txt +++ b/inc/lang/en/subscr_single.txt @@ -15,7 +15,7 @@ New Revision: @NEWPAGE@ To cancel the page notifications, log into the wiki at @DOKUWIKIURL@ then visit -@NEWPAGE@ +@SUBSCRIBE@ and unsubscribe page and/or namespace changes. -- diff --git a/inc/lang/eo/subscr_single.txt b/inc/lang/eo/subscr_single.txt index 431fd0251..e4847e8ab 100644 --- a/inc/lang/eo/subscr_single.txt +++ b/inc/lang/eo/subscr_single.txt @@ -15,7 +15,7 @@ Nova versio: @NEWPAGE@ Por nuligi la paĝinformojn, ensalutu la vikion ĉe @DOKUWIKIURL@, poste iru al -@NEWPAGE@ +@SUBSCRIBE@ kaj malabonu la paĝajn kaj/aŭ nomspacajn ŝanĝojn. -- diff --git a/inc/lang/he/subscr_single.txt b/inc/lang/he/subscr_single.txt index 123b186c8..78b551e2f 100644 --- a/inc/lang/he/subscr_single.txt +++ b/inc/lang/he/subscr_single.txt @@ -14,7 +14,7 @@ לביטול התרעות בנוגע לעמוד, יש להיכנס לאתר הוויקי בכתובת @DOKUWIKIURL@ ואז לבקר בדף -@NEWPAGE@ +@SUBSCRIBE@ ולבטל את המינוי לקבלת שינויים בדף ו/או במרחב השם. -- diff --git a/inc/lang/ia/subscr_single.txt b/inc/lang/ia/subscr_single.txt index 3d6ef7103..445df197c 100644 --- a/inc/lang/ia/subscr_single.txt +++ b/inc/lang/ia/subscr_single.txt @@ -15,7 +15,7 @@ Version nove: @NEWPAGE@ Pro cancellar le notificationes de paginas, aperi un session al wiki a @DOKUWIKIURL@ postea visita -@NEWPAGE@ +@SUBSCRIBE@ e cancella tu subscription al modificationes in paginas e/o spatios de nomines. -- diff --git a/inc/lang/it/subscr_single.txt b/inc/lang/it/subscr_single.txt index 8cde8ea0f..a8649a4ef 100644 --- a/inc/lang/it/subscr_single.txt +++ b/inc/lang/it/subscr_single.txt @@ -15,7 +15,7 @@ Nuova revisione: @NEWPAGE@ Per non ricevere più queste notifiche, collegati al wiki all'indirizzo @DOKUWIKIURL@ e poi visita -@NEWPAGE@ +@SUBSCRIBE@ e rimuovi la sottoscrizione alle modifiche della pagina o categoria. diff --git a/inc/lang/ko/subscr_single.txt b/inc/lang/ko/subscr_single.txt index 6bd1885e6..2679db393 100644 --- a/inc/lang/ko/subscr_single.txt +++ b/inc/lang/ko/subscr_single.txt @@ -14,7 +14,7 @@ 새 버전 : @NEWPAGE@ 이 문서의 알림을 취소하려면, @DOKUWIKIURL@에 로그인한 뒤 -@NEWPAGE@ 문서를 방문하여 문서나 이름공간의 구독을 취소하세요. +@SUBSCRIBE@ 문서를 방문하여 문서나 이름공간의 구독을 취소하세요. -- @DOKUWIKIURL@의 DokuWiki가 자동으로 만들어낸 메일입니다. \ No newline at end of file diff --git a/inc/lang/la/subscr_digest.txt b/inc/lang/la/subscr_digest.txt index 629213359..32d378a7e 100644 --- a/inc/lang/la/subscr_digest.txt +++ b/inc/lang/la/subscr_digest.txt @@ -12,7 +12,7 @@ Noua recensio: @NEWPAGE@ Ut paginae adnotationes deleas, in uicem ineas in @DOKUWIKIURL@, deinde uideas -@NEWPAGE@ +@SUBSCRIBE@ et paginarum generum optiones mutes. -- diff --git a/inc/lang/la/subscr_single.txt b/inc/lang/la/subscr_single.txt index 7839791ea..14285014c 100644 --- a/inc/lang/la/subscr_single.txt +++ b/inc/lang/la/subscr_single.txt @@ -15,7 +15,7 @@ Noua recensio: @NEWPAGE@ Ut paginae adnotationes deleas, in uicem ineas in @DOKUWIKIURL@, deinde uideas -@NEWPAGE@ +@SUBSCRIBE@ et paginarum et\aut generum optiones mutasa. -- diff --git a/inc/lang/no/subscr_digest.txt b/inc/lang/no/subscr_digest.txt index 6afd0cc5c..670d39d32 100644 --- a/inc/lang/no/subscr_digest.txt +++ b/inc/lang/no/subscr_digest.txt @@ -12,7 +12,7 @@ Ny versjon: @NEWPAGE@ For å avslutte varslingen, logg inn på @DOKUWIKIURL@ og gå til -@NEWPAGE@ +@SUBSCRIBE@ og avslutt abonnementet på endringer av siden eller i navnerommet. -- diff --git a/inc/lang/no/subscr_list.txt b/inc/lang/no/subscr_list.txt index 72cd307cb..860d88d2a 100644 --- a/inc/lang/no/subscr_list.txt +++ b/inc/lang/no/subscr_list.txt @@ -9,7 +9,7 @@ Her er endringene: For å avslutte varslinga, logg inn på @DOKUWIKIURL@ og gå til -@NEWPAGE@ +@SUBSCRIBE@ og avslutt abonnementet på endringer av sida eller i navnerommet. -- diff --git a/inc/lang/no/subscr_single.txt b/inc/lang/no/subscr_single.txt index 25296da58..b26b3a879 100644 --- a/inc/lang/no/subscr_single.txt +++ b/inc/lang/no/subscr_single.txt @@ -15,7 +15,7 @@ Ny versjon: @NEWPAGE@ For å avslutte varslingen, logg inn på @DOKUWIKIURL@, gå til -@NEWPAGE@ +@SUBSCRIBE@ og avslutt abonnementet på endringer av siden eller i navnerommet. -- diff --git a/inc/lang/pt/subscr_single.txt b/inc/lang/pt/subscr_single.txt index 1187b5911..469c6bfb1 100644 --- a/inc/lang/pt/subscr_single.txt +++ b/inc/lang/pt/subscr_single.txt @@ -15,7 +15,7 @@ Revisão Nova: @NEWPAGE@ Para cancelar as notificações de página, inicie sessão no wiki em @DOKUWIKIURL@, visite -@NEWPAGE@ +@SUBSCRIBE@ e des-subscreva às alterações de página e/ou espaço de nome. -- diff --git a/inc/lang/sq/subscr_single.txt b/inc/lang/sq/subscr_single.txt index 90520be4f..df28ee176 100644 --- a/inc/lang/sq/subscr_single.txt +++ b/inc/lang/sq/subscr_single.txt @@ -15,7 +15,7 @@ Rishikimi i ri: @NEWPAGE@ Për të fshirë lajmërimet e faqes, hyni në wiki tek @DOKUWIKIURL@ dhe pastaj vizitoni -@NEWPAGE@ +@SUBSCRIBE@ dhe fshini ndryshimet e faqes dhe/ose hapësirës së emrit. -- -- cgit v1.2.3 From af73bba62fb11d7872a8b108b156d451302695bd Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 26 Jan 2013 11:17:59 +0100 Subject: Clarified the behavior of the Doku_Indexer::renamePage method --- inc/indexer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index 70eac035b..37ca92055 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -339,7 +339,9 @@ class Doku_Indexer { } /** - * Rename a page in the search index without changing the indexed content + * Rename a page in the search index without changing the indexed content. This function doesn't check if the + * old or new name exists in the filesystem. It returns an error if the old page isn't in the page list of the + * indexer and it deletes all previously indexed content of the new page. * * @param string $oldpage The old page name * @param string $newpage The new page name -- cgit v1.2.3 From 25adeb91ff207452ebd6275707b8a0cc3121db6c Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 26 Jan 2013 11:18:52 +0100 Subject: Indexer: added internal deletePageNoLock method The new deletePageNoLock method is used by renamePage and avoids that the index needs to be unlocked and locked again for deleting the page. --- inc/indexer.php | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index 37ca92055..c08e438bf 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -360,10 +360,8 @@ class Doku_Indexer { $new_id = array_search($newpage, $pages); if ($new_id !== false) { - $this->unlock(); // make sure the page is not in the index anymore - $this->deletePage($newpage); - if (!$this->lock()) return 'locked'; + $this->deletePageNoLock($newpage); $pages[$new_id] = 'deleted:'.time().rand(0, 9999); } @@ -440,6 +438,7 @@ class Doku_Indexer { $this->unlock(); return true; } + /** * Remove a page from the index * @@ -453,10 +452,26 @@ class Doku_Indexer { if (!$this->lock()) return "locked"; + $result = $this->deletePageNoLock($page); + + $this->unlock(); + + return $result; + } + + /** + * Remove a page from the index without locking the index, only use this function if the index is already locked + * + * Erases entries in all known indexes. + * + * @param string $page a page name + * @return boolean the function completed successfully + * @author Tom N Harris + */ + protected function deletePageNoLock($page) { // load known documents $pid = $this->getPIDNoLock($page); if ($pid === false) { - $this->unlock(); return false; } @@ -482,7 +497,6 @@ class Doku_Indexer { } // Save the reverse index if (!$this->saveIndexKey('pageword', '', $pid, "")) { - $this->unlock(); return false; } @@ -499,7 +513,6 @@ class Doku_Indexer { $this->saveIndexKey($metaname.'_p', '', $pid, ''); } - $this->unlock(); return true; } -- cgit v1.2.3 From bc27f3e28790e9a25e9428ed275624578b3e9a2d Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 26 Jan 2013 11:22:52 +0100 Subject: Indexer: abort page rename if deletion of new id fails --- inc/indexer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/indexer.php b/inc/indexer.php index c08e438bf..e518907d7 100644 --- a/inc/indexer.php +++ b/inc/indexer.php @@ -361,7 +361,9 @@ class Doku_Indexer { $new_id = array_search($newpage, $pages); if ($new_id !== false) { // make sure the page is not in the index anymore - $this->deletePageNoLock($newpage); + if ($this->deletePageNoLock($newpage) !== true) { + return false; + } $pages[$new_id] = 'deleted:'.time().rand(0, 9999); } -- cgit v1.2.3 From 529b04166c604b1d086cbfeac1bd676227d04872 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 26 Jan 2013 12:59:15 +0100 Subject: added mediawiki password method FS#2559 This should make migrating from MediaWiki a bit easier. --- inc/PassHash.class.php | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'inc') diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php index 13be479cc..6918a04b4 100644 --- a/inc/PassHash.class.php +++ b/inc/PassHash.class.php @@ -4,7 +4,7 @@ * * This class implements various mechanisms used to hash passwords * - * @author Andreas Gohr + * @author Andreas Gohr * @license LGPL2 */ class PassHash { @@ -58,6 +58,9 @@ class PassHash { } elseif(substr($hash, 0, 6) == '{SMD5}') { $method = 'lsmd5'; $salt = substr(base64_decode(substr($hash, 6)), 16); + } elseif(preg_match('/^:B:(.+?):.{32}$/', $hash, $m)) { + $method = 'mediawiki'; + $salt = $m[1]; } elseif($len == 32) { $method = 'md5'; } elseif($len == 40) { @@ -104,7 +107,7 @@ class PassHash { * applied. * * @param string &$salt The salt, pass null if you want one generated - * @param int $len The length of the salt + * @param int $len The length of the salt */ public function init_salt(&$salt, $len = 32) { if(is_null($salt)) $salt = $this->gen_salt($len); @@ -263,7 +266,7 @@ class PassHash { * * This method was used by old MySQL systems * - * @link http://www.php.net/mysql + * @link http://www.php.net/mysql * @author * @param string $clear The clear text to hash * @return string Hashed password @@ -327,9 +330,9 @@ class PassHash { * an exception. * * @link http://www.openwall.com/phpass/ - * @param string $clear The clear text to hash - * @param string $salt The salt to use, null for random - * @param string $magic The hash identifier (P or H) + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @param string $magic The hash identifier (P or H) * @param int $compute The iteration count for new passwords * @throws Exception * @return string Hashed password @@ -430,8 +433,8 @@ class PassHash { * will break. When no salt is given, the iteration count can be set * through the $compute variable. * - * @param string $clear The clear text to hash - * @param string $salt The salt to use, null for random + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random * @param int $compute The iteration count (between 4 and 31) * @throws Exception * @return string Hashed password @@ -450,4 +453,19 @@ class PassHash { return crypt($clear, $salt); } + /** + * Password hashing method 'mediawiki' + * + * Uses salted MD5, this is referred to as Method B in MediaWiki docs. Unsalted md5 + * method 'A' is not supported. + * + * @link http://www.mediawiki.org/wiki/Manual_talk:User_table#user_password_column + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @return string Hashed password + */ + public function hash_mediawiki($clear, $salt = null) { + $this->init_salt($salt, 8); + return ':B:'.$salt.':'.md5($salt.'-'.md5($clear)); + } } -- cgit v1.2.3 From 925ad1487c71b97ed6cdb2e339a8d84abd199fef Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 26 Jan 2013 13:37:56 +0100 Subject: allow varying salt length in password hasher --- inc/PassHash.class.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'inc') diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php index 6918a04b4..15ea8cbcf 100644 --- a/inc/PassHash.class.php +++ b/inc/PassHash.class.php @@ -104,14 +104,18 @@ class PassHash { * Initialize the passed variable with a salt if needed. * * If $salt is not null, the value is kept, but the lenght restriction is - * applied. + * applied (unless, $cut is false). * * @param string &$salt The salt, pass null if you want one generated * @param int $len The length of the salt + * @param bool $cut Apply length restriction to existing salt? */ - public function init_salt(&$salt, $len = 32) { - if(is_null($salt)) $salt = $this->gen_salt($len); - if(strlen($salt) > $len) $salt = substr($salt, 0, $len); + public function init_salt(&$salt, $len = 32, $cut = true) { + if(is_null($salt)) { + $salt = $this->gen_salt($len); + $cut = true; // for new hashes we alway apply length restriction + } + if(strlen($salt) > $len && $cut) $salt = substr($salt, 0, $len); } // Password hashing methods follow below @@ -465,7 +469,7 @@ class PassHash { * @return string Hashed password */ public function hash_mediawiki($clear, $salt = null) { - $this->init_salt($salt, 8); + $this->init_salt($salt, 8, false); return ':B:'.$salt.':'.md5($salt.'-'.md5($clear)); } } -- cgit v1.2.3 From dfbe4adfd080433f91409f028935b9f9879fceca Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 26 Jan 2013 13:38:20 +0100 Subject: added SHA512 hashing method FS#2663 --- inc/PassHash.class.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'inc') diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php index 15ea8cbcf..080fb4778 100644 --- a/inc/PassHash.class.php +++ b/inc/PassHash.class.php @@ -61,6 +61,9 @@ class PassHash { } elseif(preg_match('/^:B:(.+?):.{32}$/', $hash, $m)) { $method = 'mediawiki'; $salt = $m[1]; + } elseif(preg_match('/^\$6\$(.+?)\$/', $hash, $m)) { + $method = 'sha512'; + $salt = $m[1]; } elseif($len == 32) { $method = 'md5'; } elseif($len == 40) { @@ -457,6 +460,25 @@ class PassHash { return crypt($clear, $salt); } + /** + * Password hashing method SHA512 + * + * This is only supported on PHP 5.3.2 or higher and will throw an exception if + * the needed crypt support is not available + * + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @return string Hashed password + * @throws Exception + */ + public function hash_sha512($clear, $salt = null) { + if(!defined('CRYPT_SHA512') || CRYPT_SHA512 != 1) { + throw new Exception('This PHP installation has no SHA512 support'); + } + $this->init_salt($salt, 8, false); + return crypt($clear, '$6$'.$salt.'$'); + } + /** * Password hashing method 'mediawiki' * -- cgit v1.2.3 From 10eac6731852014bcef1a0d16c59a4f6f77d8e31 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 26 Jan 2013 15:09:43 +0100 Subject: Add threading headers to change notification mails as proposed in PR #120 --- inc/subscription.php | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/subscription.php b/inc/subscription.php index 62cfd1509..31e3e08e1 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -424,9 +424,14 @@ class Subscription { $trep['DIFF'] = $tdiff; $hrep['DIFF'] = $hdiff; + $headers = array('Message-Id' => $this->getMessageID($id)); + if ($rev) { + $headers['In-Reply-To'] = $this->getMessageID($id, $rev); + } + return $this->send( $subscriber_mail, $subject, $id, - $template, $trep, $hrep + $template, $trep, $hrep, $headers ); } @@ -542,9 +547,10 @@ class Subscription { * template (in text format) * @param array $hrep Predefined parameters used to parse the * template (in HTML format), null to default to $trep + * @param array $headers Additional mail headers in the form 'name' => 'value' * @return bool */ - protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null) { + protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) { global $lang; global $conf; @@ -560,9 +566,36 @@ class Subscription { if(isset($trep['SUBSCRIBE'])) { $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false); } + + foreach ($headers as $header => $value) { + $mail->setHeader($header, $value); + } + return $mail->send(); } + /** + * Get a valid message id for a certain $id and revision (or the current revision) + * @param string $id The id of the page (or media file) the message id should be for + * @param string $rev The revision of the page, set to the current revision of the page $id if not set + * @return string + */ + protected function getMessageID($id, $rev = NULL) { + static $listid = null; + if (is_null($listid)) { + $server = parse_url(DOKU_URL, PHP_URL_HOST); + $listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server; + $listid = urlencode($listid); + $listid = strtolower(trim($listid, '.')); + } + + if (is_null($rev)) { + $rev = @filemtime(wikiFN($id)); + } + + return "<$id?rev=$rev@$listid>"; + } + /** * Default callback for COMMON_NOTIFY_ADDRESSLIST * -- cgit v1.2.3 From b5460ee2c820d95f75fd47d2f8bcbe5dddc21e7e Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 26 Jan 2013 15:10:52 +0100 Subject: Implement media subscriptions in the new subscription class --- inc/media.php | 23 ++--------------------- inc/subscription.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 21 deletions(-) (limited to 'inc') diff --git a/inc/media.php b/inc/media.php index 6335bf210..572b1177c 100644 --- a/inc/media.php +++ b/inc/media.php @@ -535,32 +535,13 @@ function media_contentcheck($file,$mime){ * Send a notify mail on uploads * * @author Andreas Gohr - * @fixme this should embed thumbnails of images in HTML version */ function media_notify($id,$file,$mime,$old_rev=false){ - global $lang; global $conf; - global $INFO; if(empty($conf['notify'])) return; //notify enabled? - $text = rawLocale('uploadmail'); - $trep = array( - 'MIME' => $mime, - 'MEDIA' => ml($id,'',true,'&',true), - 'SIZE' => filesize_h(filesize($file)), - ); - - if ($old_rev && $conf['mediarevisions']) { - $trep['OLD'] = ml($id, "rev=$old_rev", true, '&', true); - } else { - $trep['OLD'] = '---'; - } - - $mail = new Mailer(); - $mail->to($conf['notify']); - $mail->subject($lang['mail_upload'].' '.$id); - $mail->setBody($text,$trep); - return $mail->send(); + $subscription = new Subscription(); + return $subscription->send_media_diff($conf['notify'], 'uploadmail', $id, $old_rev, ''); } /** diff --git a/inc/subscription.php b/inc/subscription.php index 31e3e08e1..2989de032 100644 --- a/inc/subscription.php +++ b/inc/subscription.php @@ -435,6 +435,43 @@ class Subscription { ); } + /** + * Send the diff for some media change + * + * @fixme this should embed thumbnails of images in HTML version + * @param string $subscriber_mail The target mail address + * @param string $template Mail template ('uploadmail', ...) + * @param string $id Media file for which the notification is + * @param int|bool $rev Old revision if any + * @return bool true if successfully sent + */ + public function send_media_diff($subscriber_mail, $template, $id, $rev = false) { + global $conf; + + $file = mediaFN($id); + list($mime, $ext) = mimetype($id); + + $trep = array( + 'MIME' => $mime, + 'MEDIA' => ml($id,'',true,'&',true), + 'SIZE' => filesize_h(filesize($file)), + ); + + if ($rev && $conf['mediarevisions']) { + $trep['OLD'] = ml($id, "rev=$rev", true, '&', true); + } else { + $trep['OLD'] = '---'; + } + + $headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file))); + if ($rev) { + $headers['In-Reply-To'] = $this->getMessageID($id, $rev); + } + + $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers); + + } + /** * Send a notify mail on new registration * -- cgit v1.2.3 From fcfecb69832d5532b9c7d5362e4b7bb781c8fa11 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sat, 26 Jan 2013 16:51:16 +0000 Subject: fix for FS#2676, inserting zero length spaces into long sequences of non-breaking characters in diffs --- inc/html.php | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/html.php b/inc/html.php index 5c1c75cf6..89a8a4c7d 100644 --- a/inc/html.php +++ b/inc/html.php @@ -1154,8 +1154,7 @@ function html_diff($text='',$intro=true,$type=null){ list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev); } - $df = new Diff(explode("\n",htmlspecialchars($l_text)), - explode("\n",htmlspecialchars($r_text))); + $df = new Diff(explode("\n",hsc($l_text)),explode("\n",hsc($r_text))); if($type == 'inline'){ $tdf = new InlineDiffFormatter(); @@ -1205,12 +1204,38 @@ function html_diff($text='',$intro=true,$type=null){ - format($df)?> + format($df)); ?> ]*>|[^<> ]{12,}/','html_softbreak_callback',$diffhtml); +} + +function html_softbreak_callback($match){ + // if match is an html tag, return it intact + if ($match[0]{0} == '<') return $match[0]; + + // its a long string without a breaking character, + // make certain characters into breaking characters by inserting a + // breaking character (zero length space, U+200B / #8203) in front them. + $regex = <<< REGEX +(?(?= # start a conditional expression with a positive look ahead ... +&(\#\\d{1,4}|[[:alpha:]]{1,4});) # ... for html entities - we don't want to split them +&\#?\\w{1,4}; # yes pattern - a quicker match for the html entity, since we know we have one +| +[?/,&\#;:]+ # no pattern - any other group of 'special' characters to insert a breaking character after +) # end conditional expression +REGEX; + + return preg_replace('<'.$regex.'>xu','\0​',$match[0]); +} + /** * show warning on conflict detection * -- cgit v1.2.3 From 298a7e081c2658a706ddd08b713b3f1c420564c4 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sat, 26 Jan 2013 18:39:30 +0000 Subject: update pattern to catch more html entities --- inc/html.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/html.php b/inc/html.php index 89a8a4c7d..f72316a5e 100644 --- a/inc/html.php +++ b/inc/html.php @@ -1226,8 +1226,8 @@ function html_softbreak_callback($match){ // breaking character (zero length space, U+200B / #8203) in front them. $regex = <<< REGEX (?(?= # start a conditional expression with a positive look ahead ... -&(\#\\d{1,4}|[[:alpha:]]{1,4});) # ... for html entities - we don't want to split them -&\#?\\w{1,4}; # yes pattern - a quicker match for the html entity, since we know we have one +&\#?\\w{1,6};) # ... for html entities - we don't want to split them (ok to catch some invalid combinations) +&\#?\\w{1,6}; # yes pattern - a quicker match for the html entity, since we know we have one | [?/,&\#;:]+ # no pattern - any other group of 'special' characters to insert a breaking character after ) # end conditional expression -- cgit v1.2.3 From fe82d751808e4f0ede157e22e6c1e4b7402e2022 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sun, 27 Jan 2013 20:15:25 +0000 Subject: sp. --- inc/search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 53bd240e8..5c70b0305 100644 --- a/inc/search.php +++ b/inc/search.php @@ -16,7 +16,7 @@ if(!defined('DOKU_INC')) die('meh.'); * * @param array ref $data The results of the search are stored here * @param string $base Where to start the search - * @param callback $func Callback (function name or arayy with object,method) + * @param callback $func Callback (function name or array with object,method) * @param string $dir Current directory beyond $base * @param int $lvl Recursion Level * @author Andreas Gohr -- cgit v1.2.3 From 155e63c930bf2fdc260994a62baa80fe5f5ab764 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sun, 27 Jan 2013 20:32:41 +0000 Subject: add documentation for sort parameter, change false to 'natural' --- inc/search.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 5c70b0305..6590d7342 100644 --- a/inc/search.php +++ b/inc/search.php @@ -19,9 +19,10 @@ if(!defined('DOKU_INC')) die('meh.'); * @param callback $func Callback (function name or array with object,method) * @param string $dir Current directory beyond $base * @param int $lvl Recursion Level + * @param mixed $sort 'natural' to use natural order sorting (default); 'date' to sort by filemtime. * @author Andreas Gohr */ -function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort=false){ +function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort='natural'){ $dirs = array(); $files = array(); $filepaths = array(); -- cgit v1.2.3 From 5514a5a77e2d38cf763ed922b727ab305c8c8bb9 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sun, 27 Jan 2013 20:33:25 +0000 Subject: ensure parameter is passed down during recursion --- inc/search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 6590d7342..6fd7e9ae6 100644 --- a/inc/search.php +++ b/inc/search.php @@ -50,7 +50,7 @@ function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort='natural'){ //give directories to userfunction then recurse foreach($dirs as $dir){ if (call_user_func_array($func, array(&$data,$base,$dir,'d',$lvl,$opts))){ - search($data,$base,$func,$opts,$dir,$lvl+1); + search($data,$base,$func,$opts,$dir,$lvl+1,$sort); } } //now handle the files -- cgit v1.2.3 From 1dc5d48b104c5676df02e6bf0090e13b0e26508b Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Sun, 27 Jan 2013 20:33:38 +0000 Subject: change to use natural order sorting --- inc/search.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 6fd7e9ae6..277b17d39 100644 --- a/inc/search.php +++ b/inc/search.php @@ -42,10 +42,10 @@ function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort='natural'){ closedir($dh); if ($sort == 'date') { @array_multisort(array_map('filemtime', $filepaths), SORT_NUMERIC, SORT_DESC, $files); - } else { - sort($files); + } else /* natural */ { + natsort($files); } - sort($dirs); + natsort($dirs); //give directories to userfunction then recurse foreach($dirs as $dir){ -- cgit v1.2.3 From e1cc03e353022c84d330ba3d9d5cba92cd4878a6 Mon Sep 17 00:00:00 2001 From: Klap-in Date: Sun, 27 Jan 2013 22:45:47 +0100 Subject: $msg of loadHelper for base plugin class default to true too. --- inc/plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/plugin.php b/inc/plugin.php index 153e89407..649fc1f26 100644 --- a/inc/plugin.php +++ b/inc/plugin.php @@ -189,7 +189,7 @@ class DokuWiki_Plugin { * * @return object helper plugin object */ - function loadHelper($name, $msg){ + function loadHelper($name, $msg = true){ if (!plugin_isdisabled($name)){ $obj = plugin_load('helper',$name); }else{ -- cgit v1.2.3 From d08527ab16aedc53a592c024ec7da7b7150ad78e Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Sun, 27 Jan 2013 22:59:02 +0000 Subject: open fullscreen media manager in current namespace (FS#2420) --- inc/template.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc') diff --git a/inc/template.php b/inc/template.php index 4af35cc2b..a8960fcc4 100644 --- a/inc/template.php +++ b/inc/template.php @@ -703,6 +703,7 @@ function tpl_get_action($type) { } break; case 'media': + $params['ns'] = getNS($ID); break; default: return '[unknown %s type]'; -- cgit v1.2.3 From ec24a2df21bc25730b6d807a6930867e09f2aa58 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Tue, 29 Jan 2013 19:40:23 +0100 Subject: search: pass empty $sort to skip sorting --- inc/search.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'inc') diff --git a/inc/search.php b/inc/search.php index 277b17d39..cc3e79006 100644 --- a/inc/search.php +++ b/inc/search.php @@ -19,7 +19,7 @@ if(!defined('DOKU_INC')) die('meh.'); * @param callback $func Callback (function name or array with object,method) * @param string $dir Current directory beyond $base * @param int $lvl Recursion Level - * @param mixed $sort 'natural' to use natural order sorting (default); 'date' to sort by filemtime. + * @param mixed $sort 'natural' to use natural order sorting (default); 'date' to sort by filemtime; leave empty to skip sorting. * @author Andreas Gohr */ function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort='natural'){ @@ -40,12 +40,14 @@ function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort='natural'){ $filepaths[] = $base.'/'.$dir.'/'.$file; } closedir($dh); - if ($sort == 'date') { - @array_multisort(array_map('filemtime', $filepaths), SORT_NUMERIC, SORT_DESC, $files); - } else /* natural */ { - natsort($files); + if (!empty($sort)) { + if ($sort == 'date') { + @array_multisort(array_map('filemtime', $filepaths), SORT_NUMERIC, SORT_DESC, $files); + } else /* natural */ { + natsort($files); + } + natsort($dirs); } - natsort($dirs); //give directories to userfunction then recurse foreach($dirs as $dir){ -- cgit v1.2.3 From fc7684bc7f41c3351955f71b8a94a73bb11aa358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Corinth?= Date: Wed, 30 Jan 2013 14:36:09 +0100 Subject: Added the title attribute for namespace-links --- inc/html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/html.php b/inc/html.php index 5c1c75cf6..3124f4b1d 100644 --- a/inc/html.php +++ b/inc/html.php @@ -859,7 +859,7 @@ function html_list_index($item){ $base = ':'.$item['id']; $base = substr($base,strrpos($base,':')+1); if($item['type']=='d'){ - $ret .= ''; + $ret .= ''; $ret .= $base; $ret .= ''; }else{ -- cgit v1.2.3 From 957e84c6d04b3287613860d79d564d971531db98 Mon Sep 17 00:00:00 2001 From: Rodrigo Rega Date: Fri, 1 Feb 2013 09:37:45 +0100 Subject: Galician language update --- inc/lang/gl/lang.php | 12 ++++++------ inc/lang/gl/mailwrap.html | 2 +- inc/lang/gl/resetpwd.txt | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'inc') diff --git a/inc/lang/gl/lang.php b/inc/lang/gl/lang.php index a33aba72e..7cc06a833 100644 --- a/inc/lang/gl/lang.php +++ b/inc/lang/gl/lang.php @@ -5,7 +5,7 @@ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Medúlio * @author Oscar M. Lage - * @author Leandro Regueiro + * @author Rodrigo Rega */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -41,7 +41,7 @@ $lang['btn_backtomedia'] = 'Volver á Selección de Arquivos-Media'; $lang['btn_subscribe'] = 'Avísame dos trocos na páxina'; $lang['btn_profile'] = 'Actualizar Perfil'; $lang['btn_reset'] = 'Reiniciar'; -$lang['btn_resendpwd'] = 'Definir novo contrasinal'; +$lang['btn_resendpwd'] = 'Establecer novo contrasinal'; $lang['btn_draft'] = 'Editar borrador'; $lang['btn_recover'] = 'Recuperar borrador'; $lang['btn_draftdel'] = 'Eliminar borrador'; @@ -78,7 +78,7 @@ $lang['profnoempty'] = 'Non se permite un nome ou un enderezo de corre $lang['profchanged'] = 'Perfil de usuario actualizado correctamente.'; $lang['pwdforget'] = 'Esqueceches o teu contrasinal? Consegue un novo'; $lang['resendna'] = 'Este wiki non permite o reenvío de contrasinais.'; -$lang['resendpwd'] = 'Definir novo contrasinal para'; +$lang['resendpwd'] = 'Establecer novo contrasinal para'; $lang['resendpwdmissing'] = 'Sentímolo, tes que cubrir todos os campos.'; $lang['resendpwdnouser'] = 'Sentímolo, non atopamos este usuario no noso banco de datos.'; $lang['resendpwdbadauth'] = 'Sentímolo, mais este código de autorización non é válido. Asegúrate de que usaches a ligazón completa de confirmación.'; @@ -91,7 +91,7 @@ $lang['searchmedia_in'] = 'Procurar en %s'; $lang['txt_upload'] = 'Escolle o arquivo para subir'; $lang['txt_filename'] = 'Subir como (opcional)'; $lang['txt_overwrt'] = 'Sobrescribir arquivo existente'; -$lang['maxuploadsize'] = 'Envío máx. de %s por ficheiro.'; +$lang['maxuploadsize'] = 'Subida máxima %s por arquivo.'; $lang['lockedby'] = 'Bloqueado actualmente por'; $lang['lockexpire'] = 'O bloqueo remata o'; $lang['js']['willexpire'] = 'O teu bloqueo para editares esta páxina vai caducar nun minuto.\nPara de evitar conflitos, emprega o botón de previsualización para reiniciares o contador do tempo de bloqueo.'; @@ -190,7 +190,7 @@ $lang['tools'] = 'Ferramentas'; $lang['user_tools'] = 'Ferramentas de usuario'; $lang['site_tools'] = 'Ferramentas do sitio'; $lang['page_tools'] = 'Ferramentas de páxina'; -$lang['skip_to_content'] = 'saltar ao contido'; +$lang['skip_to_content'] = 'Pasar ao contido'; $lang['sidebar'] = 'Barra lateral'; $lang['mail_newpage'] = 'páxina engadida:'; $lang['mail_changed'] = 'páxina mudada:'; @@ -262,7 +262,7 @@ $lang['subscr_style_digest'] = 'correo-e con resumo de trocos para cada páxin $lang['subscr_style_list'] = 'lista de páxinas mudadas dende o último correo-e'; $lang['authmodfailed'] = 'Configuración de autenticación de usuario incorrecta. Por favor, informa ao Administrador do teu Wiki.'; $lang['authtempfail'] = 'A autenticación de usuario non está dispoñible de xeito temporal. De persistir esta situación, por favor, informa ao Administrador do teu Wiki.'; -$lang['authpwdexpire'] = 'O seu contrasinal vai caducar en %d días, razón pola cal debería cambialo axiña.'; +$lang['authpwdexpire'] = 'A túa contrasinal expirará en %d días, deberías cambiala pronto.'; $lang['i_chooselang'] = 'Escolle o teu idioma'; $lang['i_installer'] = 'Instalador do DokuWiki'; $lang['i_wikiname'] = 'Nome do Wiki'; diff --git a/inc/lang/gl/mailwrap.html b/inc/lang/gl/mailwrap.html index 05ef4175a..19927c117 100644 --- a/inc/lang/gl/mailwrap.html +++ b/inc/lang/gl/mailwrap.html @@ -8,6 +8,6 @@ @HTMLBODY@

-Esta mensaxe foi xerada por DokuWiki en @DOKUWIKIURL@. +Este correo era xerado por DokuWiki en @DOKUWIKIURL@. \ No newline at end of file diff --git a/inc/lang/gl/resetpwd.txt b/inc/lang/gl/resetpwd.txt index 7b662d8f5..d3d64e90d 100644 --- a/inc/lang/gl/resetpwd.txt +++ b/inc/lang/gl/resetpwd.txt @@ -1,3 +1,3 @@ -====== Definir novo contrasinal ====== +====== Establecer novo contrasinal ====== -Introduza un novo contrasinal para a súa conta neste wiki. \ No newline at end of file +Por favor introduzca un novo contrasinal para a súa conta neste wiki. \ No newline at end of file -- cgit v1.2.3 From 58e982182a58f0728ae6d223fd65016aded52b46 Mon Sep 17 00:00:00 2001 From: Vasileios Karavasilis Date: Fri, 1 Feb 2013 15:41:02 +0100 Subject: Greek language update --- inc/lang/el/lang.php | 25 ++++++++++++++++++++----- inc/lang/el/mailwrap.html | 13 +++++++++++++ inc/lang/el/resetpwd.txt | 3 +++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 inc/lang/el/mailwrap.html create mode 100644 inc/lang/el/resetpwd.txt (limited to 'inc') diff --git a/inc/lang/el/lang.php b/inc/lang/el/lang.php index b6cdc38c1..55b70074f 100644 --- a/inc/lang/el/lang.php +++ b/inc/lang/el/lang.php @@ -8,6 +8,7 @@ * @author Konstantinos Koryllos * @author George Petsagourakis * @author Petros Vidalis + * @author Vasileios Karavasilis vasileioskaravasilis@gmail.com */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -43,6 +44,7 @@ $lang['btn_backtomedia'] = 'Επιστροφή στην επιλογή α $lang['btn_subscribe'] = 'Εγγραφή σε λήψη ενημερώσεων σελίδας'; $lang['btn_profile'] = 'Επεξεργασία προφίλ'; $lang['btn_reset'] = 'Ακύρωση'; +$lang['btn_resendpwd'] = 'Εισαγωγή νέου κωδικού'; $lang['btn_draft'] = 'Επεξεργασία αυτόματα αποθηκευμένης σελίδας'; $lang['btn_recover'] = 'Επαναφορά αυτόματα αποθηκευμένης σελίδας'; $lang['btn_draftdel'] = 'Διαγραφή αυτόματα αποθηκευμένης σελίδας'; @@ -79,6 +81,7 @@ $lang['profnoempty'] = 'Δεν επιτρέπεται κενό όνο $lang['profchanged'] = 'Το προφίλ χρήστη τροποποιήθηκε επιτυχώς.'; $lang['pwdforget'] = 'Ξεχάσατε το κωδικό σας; Αποκτήστε νέο.'; $lang['resendna'] = 'Αυτό το wiki δεν υποστηρίζει την εκ\' νέου αποστολή κωδικών.'; +$lang['resendpwd'] = 'Εισαγωγή νέου ωδικού για'; $lang['resendpwdmissing'] = 'Πρέπει να συμπληρώσετε όλα τα πεδία.'; $lang['resendpwdnouser'] = 'Αυτός ο χρήστης δεν υπάρχει στα αρχεία μας.'; $lang['resendpwdbadauth'] = 'Αυτός ο κωδικός ενεργοποίησης δεν είναι έγκυρος.'; @@ -91,6 +94,7 @@ $lang['searchmedia_in'] = 'Αναζήτηση σε %s'; $lang['txt_upload'] = 'Επιλέξτε αρχείο για φόρτωση'; $lang['txt_filename'] = 'Επιλέξτε νέο όνομα αρχείου (προαιρετικό)'; $lang['txt_overwrt'] = 'Αντικατάσταση υπάρχοντος αρχείου'; +$lang['maxuploadsize'] = 'Μέγιστο μέγεθος αρχείου: %s.'; $lang['lockedby'] = 'Προσωρινά κλειδωμένο από'; $lang['lockexpire'] = 'Το κλείδωμα λήγει στις'; $lang['js']['willexpire'] = 'Το κλείδωμά σας για την επεξεργασία αυτής της σελίδας θα λήξει σε ένα λεπτό.\n Για να το ανανεώσετε χρησιμοποιήστε την Προεπισκόπηση.'; @@ -185,6 +189,12 @@ $lang['external_edit'] = 'εξωτερική τροποποίηση'; $lang['summary'] = 'Επεξεργασία σύνοψης'; $lang['noflash'] = 'Το Adobe Flash Plugin απαιτείται για την προβολή αυτού του στοιχείου.'; $lang['download'] = 'Λήψη Κώδικα'; +$lang['tools'] = 'Εργαλεία'; +$lang['user_tools'] = 'Εργαλεία Χρήστη'; +$lang['site_tools'] = 'Εργαλεία ιστότοπου'; +$lang['page_tools'] = 'Εργαλεία ιστοσελίδας'; +$lang['skip_to_content'] = 'παράληψη περιεχομένων'; +$lang['sidebar'] = 'Sidebar'; $lang['mail_newpage'] = 'σελίδα προστέθηκε:'; $lang['mail_changed'] = 'σελίδα τροποποιήθηκε:'; $lang['mail_subscribe_list'] = 'σελίδες που άλλαξαν στον φάκελο:'; @@ -255,6 +265,7 @@ $lang['subscr_style_digest'] = 'συνοπτικό email αλλαγών της $lang['subscr_style_list'] = 'λίστα σελίδων με αλλαγές μετά από το τελευταίο email (κάθε %.2f μέρες)'; $lang['authmodfailed'] = 'Κακή ρύθμιση λίστας χρηστών. Παρακαλούμε ενημερώστε τον διαχειριστή του wiki.'; $lang['authtempfail'] = 'Η συνδεση χρηστών είναι απενεργοποιημένη αυτή την στιγμή. Αν αυτό διαρκέσει για πολύ, παρακαλούμε ενημερώστε τον διαχειριστή του wiki.'; +$lang['authpwdexpire'] = 'Ο κωδικός πρόσβασης θα λήξει σε %s ημέρες. Προτείνουμε να τον αλλάξετε σύντομα.'; $lang['i_chooselang'] = 'Επιλογή γλώσσας'; $lang['i_installer'] = 'Οδηγός εγκατάστασης DokuWiki'; $lang['i_wikiname'] = 'Ονομασία wiki'; @@ -270,8 +281,8 @@ $lang['i_confexists'] = '%s υπάρχει ήδη'; $lang['i_writeerr'] = 'Δεν είναι δυνατή η δημιουργία του %s. Πρέπει να διορθώσετε τα δικαιώματα πρόσβασης αυτού του φακέλου/αρχείου και να δημιουργήσετε το αρχείο χειροκίνητα!'; $lang['i_badhash'] = 'Μη αναγνωρίσιμο ή τροποποιημένο αρχείο dokuwiki.php (hash=%s)'; $lang['i_badval'] = '%s - λάθος ή ανύπαρκτη τιμή'; -$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; -$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; +$lang['i_success'] = 'Η εγκατάσταση ολοκληρώθηκε επιτυχώς. Μπορείτε πλέον να διαγράψετε το αρχείο install.php. Συνεχίστε στο νέο σας DokuWiki.'; +$lang['i_failure'] = 'Εμφανίστηκαν κάποια προβλήματα στη διαδικασία ανανέωσης των αρχείων ρυθμίσεων. Πιθανόν να χρειάζεται να τα τροποποιήσετε χειροκίνητα ώστε να μπορείτε να χρησιμοποιήσετε το νέο σας DokuWiki.'; $lang['i_policy'] = 'Αρχική πολιτική Λίστας Δικαιωμάτων Πρόσβασης - ACL'; $lang['i_pol0'] = 'Ανοιχτό Wiki (όλοι μπορούν να διαβάσουν ή να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; $lang['i_pol1'] = 'Δημόσιο Wiki (όλοι μπορούν να διαβάσουν σελίδες αλλά μόνο οι εγγεγραμμένοι χρήστες μπορούν να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; @@ -289,16 +300,20 @@ $lang['seconds'] = 'πριν %d δευτερόλεπτα'; $lang['wordblock'] = 'Η αλλαγή σας δεν αποθηκεύτηκε γιατί περιείχε spam.'; $lang['media_uploadtab'] = 'Φόρτωση'; $lang['media_searchtab'] = 'Αναζήτηση'; +$lang['media_file'] = 'Αρχείο'; $lang['media_viewtab'] = 'Εμφάνιση'; $lang['media_edittab'] = 'Επεξεργασία'; $lang['media_historytab'] = 'Ιστορικό'; -$lang['media_thumbsview'] = 'Προεπισκόπιση'; -$lang['media_listview'] = 'Λίστα'; -$lang['media_sort'] = 'Ταξινόμιση'; +$lang['media_list_thumbs'] = 'Μικρογραφίες'; +$lang['media_list_rows'] = 'Γραμμές'; $lang['media_sort_name'] = 'ανά όνομα'; $lang['media_sort_date'] = 'ανά ημερομηνία'; +$lang['media_namespaces'] = 'Επιλογή namespace'; +$lang['media_files'] = 'Αρχεία στο %s φάκελο'; $lang['media_upload'] = 'Φόρτωση στο %s φάκελο.'; $lang['media_search'] = 'Αναζήτηση στο %s φάκελο.'; +$lang['media_view'] = '%s'; +$lang['media_viewold'] = '%s στα %s'; $lang['media_edit'] = 'Επεξεργασία'; $lang['media_history'] = 'Αυτές είναι οι παλαιότερες αναθεωρήσεις του αρχείου.'; $lang['media_meta_edited'] = 'τα μεταδεδομένα επεξεργάστηκαν'; diff --git a/inc/lang/el/mailwrap.html b/inc/lang/el/mailwrap.html new file mode 100644 index 000000000..b2e655789 --- /dev/null +++ b/inc/lang/el/mailwrap.html @@ -0,0 +1,13 @@ + + +@TITLE@ + + + + +@HTMLBODY@ + +

+Το email έχει δημιουργηθεί από το DokuWiki στις @DOKUWIKIURL@. + + \ No newline at end of file diff --git a/inc/lang/el/resetpwd.txt b/inc/lang/el/resetpwd.txt new file mode 100644 index 000000000..0d26d05fa --- /dev/null +++ b/inc/lang/el/resetpwd.txt @@ -0,0 +1,3 @@ +====== Εισάγετε νέο κωδικό πρόσβασης ====== + +Παρακαλούμε, εισάγετε έναν νέο κωδικό πρόσβασης για τον λογαριασμό σας. \ No newline at end of file -- cgit v1.2.3 From fca4df27d2d1c92086bd70be4407a732ef4aadc2 Mon Sep 17 00:00:00 2001 From: Anika Henke Date: Sun, 3 Feb 2013 17:29:49 +0000 Subject: added 'home' class to first link in hierarchical breadcrumbs --- inc/template.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'inc') diff --git a/inc/template.php b/inc/template.php index a8960fcc4..2c083c964 100644 --- a/inc/template.php +++ b/inc/template.php @@ -840,7 +840,9 @@ function tpl_youarehere($sep = ' » ') { echo ''.$lang['youarehere'].': '; // always print the startpage + echo ''; tpl_pagelink(':'.$conf['start']); + echo ''; // print intermediate namespace links $part = ''; -- cgit v1.2.3 From 58bedc8a310955dbc00e738bec1dc4f442eaff4e Mon Sep 17 00:00:00 2001 From: borekb Date: Wed, 30 Jan 2013 21:51:39 +0100 Subject: Updated idfilter() function for IIS There is a condition inside idfilter() function that helps Apache on Windows to properly handle colon as a namespace separator by replacing it with semicolon. However, this is not necessary on Microsoft IIS server so the condition was improved. --- inc/common.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index bc49e76b2..a270e4eaf 100644 --- a/inc/common.php +++ b/inc/common.php @@ -311,7 +311,7 @@ function breadcrumbs() { * * This is run on a ID before it is outputted somewhere * currently used to replace the colon with something else - * on Windows systems and to have proper URL encoding + * on Windows non-IIS systems and to have proper URL encoding * * Urlencoding is ommitted when the second parameter is false * @@ -322,7 +322,8 @@ function idfilter($id, $ue = true) { if($conf['useslash'] && $conf['userewrite']) { $id = strtr($id, ':', '/'); } elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && - $conf['userewrite'] + $conf['userewrite'] && + strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') === false ) { $id = strtr($id, ':', ';'); } -- cgit v1.2.3 From 907f24f7352841e6c3030e143ec75661c55244d8 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Feb 2013 21:12:06 +0100 Subject: added comment on use of whitelist vs blacklist --- inc/common.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'inc') diff --git a/inc/common.php b/inc/common.php index a270e4eaf..db39affc6 100644 --- a/inc/common.php +++ b/inc/common.php @@ -311,7 +311,11 @@ function breadcrumbs() { * * This is run on a ID before it is outputted somewhere * currently used to replace the colon with something else - * on Windows non-IIS systems and to have proper URL encoding + * on Windows (non-IIS) systems and to have proper URL encoding + * + * See discussions at https://github.com/splitbrain/dokuwiki/pull/84 and + * https://github.com/splitbrain/dokuwiki/pull/173 why we use a whitelist of + * unaffected servers instead of blacklisting affected servers here. * * Urlencoding is ommitted when the second parameter is false * -- cgit v1.2.3 From b75f4cc2b25d0b6587df71ce6a246882e36ff10b Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Feb 2013 22:13:13 +0100 Subject: upgraded SimplePie to 1.3.1 FS#2708 --- inc/FeedParser.php | 8 +- inc/SimplePie.php | 24245 ++++++++++++++++++++++++++++----------------------- 2 files changed, 13361 insertions(+), 10892 deletions(-) (limited to 'inc') diff --git a/inc/FeedParser.php b/inc/FeedParser.php index e5f1fb636..96d32e83f 100644 --- a/inc/FeedParser.php +++ b/inc/FeedParser.php @@ -15,8 +15,8 @@ class FeedParser extends SimplePie { /** * Constructor. Set some defaults */ - function FeedParser(){ - $this->SimplePie(); + function __construct(){ + parent::__construct(); $this->enable_cache(false); $this->set_file_class('FeedParser_File'); } @@ -47,8 +47,8 @@ class FeedParser_File extends SimplePie_File { * * We ignore all given parameters - they are set in DokuHTTPClient */ - function FeedParser_File($url, $timeout=10, $redirects=5, - $headers=null, $useragent=null, $force_fsockopen=false) { + function __construct($url, $timeout=10, $redirects=5, + $headers=null, $useragent=null, $force_fsockopen=false) { $this->http = new DokuHTTPClient(); $this->success = $this->http->sendRequest($url); diff --git a/inc/SimplePie.php b/inc/SimplePie.php index 10d8141bd..fd69b4b09 100644 --- a/inc/SimplePie.php +++ b/inc/SimplePie.php @@ -5,7 +5,10 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2011, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Please note: This file is automatically generated by a build script. The + * full original source is always available from http://simplepie.org/ + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,15 +36,13 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.2.1 - * @copyright 2004-2011 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie - * @link http://simplepie.org/support/ Please submit all bug reports and feature requests to the SimplePie forums * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @todo phpDoc comments */ /** @@ -52,12 +53,13 @@ define('SIMPLEPIE_NAME', 'SimplePie'); /** * SimplePie Version */ -define('SIMPLEPIE_VERSION', '1.2.1-dev'); +define('SIMPLEPIE_VERSION', '1.3.1'); /** * SimplePie Build + * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc) */ -define('SIMPLEPIE_BUILD', '20111015034325'); +define('SIMPLEPIE_BUILD', '20121030175911'); /** * SimplePie Website URL @@ -334,10 +336,30 @@ define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); /** - * Wrong Media RSS Namespace + * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); +/** + * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss'); + +/** + * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/'); + +/** + * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss'); + +/** + * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/'); + /** * iTunes RSS Namespace */ @@ -353,11 +375,6 @@ define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); */ define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); -/** - * Whether we're running on PHP5 - */ -define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>=')); - /** * No file source */ @@ -392,6 +409,7 @@ define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); * SimplePie * * @package SimplePie + * @subpackage API */ class SimplePie { @@ -399,55 +417,55 @@ class SimplePie * @var array Raw data * @access private */ - var $data = array(); + public $data = array(); /** * @var mixed Error string * @access private */ - var $error; + public $error; /** * @var object Instance of SimplePie_Sanitize (or other class) * @see SimplePie::set_sanitize_class() * @access private */ - var $sanitize; + public $sanitize; /** * @var string SimplePie Useragent * @see SimplePie::set_useragent() * @access private */ - var $useragent = SIMPLEPIE_USERAGENT; + public $useragent = SIMPLEPIE_USERAGENT; /** * @var string Feed URL * @see SimplePie::set_feed_url() * @access private */ - var $feed_url; + public $feed_url; /** * @var object Instance of SimplePie_File to use as a feed * @see SimplePie::set_file() * @access private */ - var $file; + public $file; /** * @var string Raw feed data * @see SimplePie::set_raw_data() * @access private */ - var $raw_data; + public $raw_data; /** * @var int Timeout for fetching remote files * @see SimplePie::set_timeout() * @access private */ - var $timeout = 10; + public $timeout = 10; /** * @var bool Forces fsockopen() to be used for remote files instead @@ -455,7 +473,7 @@ class SimplePie * @see SimplePie::force_fsockopen() * @access private */ - var $force_fsockopen = false; + public $force_fsockopen = false; /** * @var bool Force the given data/URL to be treated as a feed no matter what @@ -463,56 +481,49 @@ class SimplePie * @see SimplePie::force_feed() * @access private */ - var $force_feed = false; - - /** - * @var bool Enable/Disable XML dump - * @see SimplePie::enable_xml_dump() - * @access private - */ - var $xml_dump = false; + public $force_feed = false; /** * @var bool Enable/Disable Caching * @see SimplePie::enable_cache() * @access private */ - var $cache = true; + public $cache = true; /** * @var int Cache duration (in seconds) * @see SimplePie::set_cache_duration() * @access private */ - var $cache_duration = 3600; + public $cache_duration = 3600; /** * @var int Auto-discovery cache duration (in seconds) * @see SimplePie::set_autodiscovery_cache_duration() * @access private */ - var $autodiscovery_cache_duration = 604800; // 7 Days. + public $autodiscovery_cache_duration = 604800; // 7 Days. /** * @var string Cache location (relative to executing script) * @see SimplePie::set_cache_location() * @access private */ - var $cache_location = './cache'; + public $cache_location = './cache'; /** * @var string Function that creates the cache filename * @see SimplePie::set_cache_name_function() * @access private */ - var $cache_name_function = 'md5'; + public $cache_name_function = 'md5'; /** * @var bool Reorder feed by date descending * @see SimplePie::enable_order_by_date() * @access private */ - var $order_by_date = true; + public $order_by_date = true; /** * @var mixed Force input encoding to be set to the follow value @@ -520,246 +531,132 @@ class SimplePie * @see SimplePie::set_input_encoding() * @access private */ - var $input_encoding = false; + public $input_encoding = false; /** * @var int Feed Autodiscovery Level * @see SimplePie::set_autodiscovery_level() * @access private */ - var $autodiscovery = SIMPLEPIE_LOCATOR_ALL; - - /** - * @var string Class used for caching feeds - * @see SimplePie::set_cache_class() - * @access private - */ - var $cache_class = 'SimplePie_Cache'; - - /** - * @var string Class used for locating feeds - * @see SimplePie::set_locator_class() - * @access private - */ - var $locator_class = 'SimplePie_Locator'; - - /** - * @var string Class used for parsing feeds - * @see SimplePie::set_parser_class() - * @access private - */ - var $parser_class = 'SimplePie_Parser'; - - /** - * @var string Class used for fetching feeds - * @see SimplePie::set_file_class() - * @access private - */ - var $file_class = 'SimplePie_File'; - - /** - * @var string Class used for items - * @see SimplePie::set_item_class() - * @access private - */ - var $item_class = 'SimplePie_Item'; - - /** - * @var string Class used for authors - * @see SimplePie::set_author_class() - * @access private - */ - var $author_class = 'SimplePie_Author'; - - /** - * @var string Class used for categories - * @see SimplePie::set_category_class() - * @access private - */ - var $category_class = 'SimplePie_Category'; - - /** - * @var string Class used for enclosures - * @see SimplePie::set_enclosures_class() - * @access private - */ - var $enclosure_class = 'SimplePie_Enclosure'; - - /** - * @var string Class used for Media RSS captions - * @see SimplePie::set_caption_class() - * @access private - */ - var $caption_class = 'SimplePie_Caption'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_copyright_class() - * @access private - */ - var $copyright_class = 'SimplePie_Copyright'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_credit_class() - * @access private - */ - var $credit_class = 'SimplePie_Credit'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_rating_class() - * @access private - */ - var $rating_class = 'SimplePie_Rating'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_restriction_class() - * @access private - */ - var $restriction_class = 'SimplePie_Restriction'; - - /** - * @var string Class used for content-type sniffing - * @see SimplePie::set_content_type_sniffer_class() - * @access private - */ - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - /** - * @var string Class used for item sources. - * @see SimplePie::set_source_class() - * @access private - */ - var $source_class = 'SimplePie_Source'; + public $autodiscovery = SIMPLEPIE_LOCATOR_ALL; /** - * @var mixed Set javascript query string parameter (false, or - * anything type-cast to false, disables this feature) - * @see SimplePie::set_javascript() - * @access private + * Class registry object + * + * @var SimplePie_Registry */ - var $javascript = 'js'; + public $registry; /** * @var int Maximum number of feeds to check with autodiscovery * @see SimplePie::set_max_checked_feeds() * @access private */ - var $max_checked_feeds = 10; + public $max_checked_feeds = 10; /** * @var array All the feeds found during the autodiscovery process * @see SimplePie::get_all_discovered_feeds() * @access private */ - var $all_discovered_feeds = array(); - - /** - * @var string Web-accessible path to the handler_favicon.php file. - * @see SimplePie::set_favicon_handler() - * @access private - */ - var $favicon_handler = ''; + public $all_discovered_feeds = array(); /** * @var string Web-accessible path to the handler_image.php file. * @see SimplePie::set_image_handler() * @access private */ - var $image_handler = ''; + public $image_handler = ''; /** * @var array Stores the URLs when multiple feeds are being initialized. * @see SimplePie::set_feed_url() * @access private */ - var $multifeed_url = array(); + public $multifeed_url = array(); /** * @var array Stores SimplePie objects when multiple feeds initialized. * @access private */ - var $multifeed_objects = array(); + public $multifeed_objects = array(); /** * @var array Stores the get_object_vars() array for use with multifeeds. * @see SimplePie::set_feed_url() * @access private */ - var $config_settings = null; + public $config_settings = null; /** * @var integer Stores the number of items to return per-feed with multifeeds. * @see SimplePie::set_item_limit() * @access private */ - var $item_limit = 0; + public $item_limit = 0; /** * @var array Stores the default attributes to be stripped by strip_attributes(). * @see SimplePie::strip_attributes() * @access private */ - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); /** * @var array Stores the default tags to be stripped by strip_htmltags(). * @see SimplePie::strip_htmltags() * @access private */ - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); /** * The SimplePie class contains feed level data and options * - * There are two ways that you can create a new SimplePie object. The first - * is by passing a feed URL as a parameter to the SimplePie constructor - * (as well as optionally setting the cache location and cache expiry). This - * will initialise the whole feed with all of the default settings, and you - * can begin accessing methods and properties immediately. - * - * The second way is to create the SimplePie object with no parameters - * at all. This will enable you to set configuration options. After setting + * To use SimplePie, create the SimplePie object with no parameters. You can + * then set configuration options using the provided methods. After setting * them, you must initialise the feed using $feed->init(). At that point the - * object's methods and properties will be available to you. This format is - * what is used throughout this documentation. + * object's methods and properties will be available to you. + * + * Previously, it was possible to pass in the feed URL along with cache + * options directly into the constructor. This has been removed as of 1.3 as + * it caused a lot of confusion. * - * @access public * @since 1.0 Preview Release - * @param string $feed_url This is the URL you want to parse. - * @param string $cache_location This is where you want the cache to be stored. - * @param int $cache_duration This is the number of seconds that you want to store the cache file for. */ - function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null) + public function __construct() { - // Other objects, instances created here so we can set options on them - $this->sanitize = new SimplePie_Sanitize; - - // Set options if they're passed to the constructor - if ($cache_location !== null) + if (version_compare(PHP_VERSION, '5.2', '<')) { - $this->set_cache_location($cache_location); + trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.'); + die(); } - if ($cache_duration !== null) - { - $this->set_cache_duration($cache_duration); - } + // Other objects, instances created here so we can set options on them + $this->sanitize = new SimplePie_Sanitize(); + $this->registry = new SimplePie_Registry(); - // Only init the script if we're passed a feed URL - if ($feed_url !== null) + if (func_num_args() > 0) { - $this->set_feed_url($feed_url); - $this->init(); + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level); + + $args = func_get_args(); + switch (count($args)) { + case 3: + $this->set_cache_duration($args[2]); + case 2: + $this->set_cache_location($args[1]); + case 1: + $this->set_feed_url($args[0]); + $this->init(); + } } } /** * Used for converting object to a string */ - function __toString() + public function __toString() { return md5(serialize($this->data)); } @@ -767,7 +664,7 @@ class SimplePie /** * Remove items that link back to this before destroying this object */ - function __destruct() + public function __destruct() { if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) { @@ -791,20 +688,21 @@ class SimplePie } /** - * Force the given data/URL to be treated as a feed no matter what it - * appears like + * Force the given data/URL to be treated as a feed + * + * This tells SimplePie to ignore the content-type provided by the server. + * Be careful when using this option, as it will also disable autodiscovery. * - * @access public * @since 1.1 * @param bool $enable Force the given data/URL to be treated as a feed */ - function force_feed($enable = false) + public function force_feed($enable = false) { $this->force_feed = (bool) $enable; } /** - * This is the URL of the feed you want to parse. + * Set the URL of the feed you want to parse * * This allows you to enter the URL of the feed you want to parse, or the * website you want to try to use auto-discovery on. This takes priority @@ -814,37 +712,35 @@ class SimplePie * of a string for the $url. Remember that with each additional feed comes * additional processing and resources. * - * @access public * @since 1.0 Preview Release - * @param mixed $url This is the URL (or array of URLs) that you want to parse. - * @see SimplePie::set_raw_data() + * @see set_raw_data() + * @param string|array $url This is the URL (or array of URLs) that you want to parse. */ - function set_feed_url($url) + public function set_feed_url($url) { + $this->multifeed_url = array(); if (is_array($url)) { - $this->multifeed_url = array(); foreach ($url as $value) { - $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1); + $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1)); } } else { - $this->feed_url = SimplePie_Misc::fix_protocol($url, 1); + $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); } } /** - * Provides an instance of SimplePie_File to use as a feed + * Set an instance of {@see SimplePie_File} to use as a feed * - * @access public - * @param object &$file Instance of SimplePie_File (or subclass) + * @param SimplePie_File &$file * @return bool True on success, false on failure */ - function set_file(&$file) + public function set_file(&$file) { - if (is_a($file, 'SimplePie_File')) + if ($file instanceof SimplePie_File) { $this->feed_url = $file->url; $this->file =& $file; @@ -854,138 +750,113 @@ class SimplePie } /** + * Set the raw XML data to parse + * * Allows you to use a string of RSS/Atom data instead of a remote feed. * * If you have a feed available as a string in PHP, you can tell SimplePie * to parse that data string instead of a remote feed. Any set feed URL * takes precedence. * - * @access public * @since 1.0 Beta 3 * @param string $data RSS or Atom data as a string. - * @see SimplePie::set_feed_url() + * @see set_feed_url() */ - function set_raw_data($data) + public function set_raw_data($data) { $this->raw_data = $data; } /** - * Allows you to override the default timeout for fetching remote feeds. + * Set the the default timeout for fetching remote feeds * * This allows you to change the maximum time the feed's server to respond * and send the feed back. * - * @access public * @since 1.0 Beta 3 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. */ - function set_timeout($timeout = 10) + public function set_timeout($timeout = 10) { $this->timeout = (int) $timeout; } /** - * Forces SimplePie to use fsockopen() instead of the preferred cURL - * functions. + * Force SimplePie to use fsockopen() instead of cURL * - * @access public * @since 1.0 Beta 3 * @param bool $enable Force fsockopen() to be used */ - function force_fsockopen($enable = false) + public function force_fsockopen($enable = false) { $this->force_fsockopen = (bool) $enable; } /** - * Outputs the raw XML content of the feed, after it has gone through - * SimplePie's filters. - * - * Used only for debugging, this function will output the XML content as - * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up - * before trying to parse it. Many parts of the feed are re-written in - * memory, and in the end, you have a parsable feed. XML dump shows you the - * actual XML that SimplePie tries to parse, which may or may not be very - * different from the original feed. - * - * @access public - * @since 1.0 Preview Release - * @param bool $enable Enable XML dump - */ - function enable_xml_dump($enable = false) - { - $this->xml_dump = (bool) $enable; - } - - /** - * Enables/disables caching in SimplePie. + * Enable/disable caching in SimplePie. * * This option allows you to disable caching all-together in SimplePie. * However, disabling the cache can lead to longer load times. * - * @access public * @since 1.0 Preview Release * @param bool $enable Enable caching */ - function enable_cache($enable = true) + public function enable_cache($enable = true) { $this->cache = (bool) $enable; } /** - * Set the length of time (in seconds) that the contents of a feed - * will be cached. + * Set the length of time (in seconds) that the contents of a feed will be + * cached * - * @access public - * @param int $seconds The feed content cache duration. + * @param int $seconds The feed content cache duration */ - function set_cache_duration($seconds = 3600) + public function set_cache_duration($seconds = 3600) { $this->cache_duration = (int) $seconds; } /** - * Set the length of time (in seconds) that the autodiscovered feed - * URL will be cached. + * Set the length of time (in seconds) that the autodiscovered feed URL will + * be cached * - * @access public * @param int $seconds The autodiscovered feed URL cache duration. */ - function set_autodiscovery_cache_duration($seconds = 604800) + public function set_autodiscovery_cache_duration($seconds = 604800) { $this->autodiscovery_cache_duration = (int) $seconds; } /** - * Set the file system location where the cached files should be stored. + * Set the file system location where the cached files should be stored * - * @access public * @param string $location The file system location. */ - function set_cache_location($location = './cache') + public function set_cache_location($location = './cache') { $this->cache_location = (string) $location; } /** - * Determines whether feed items should be sorted into reverse chronological order. + * Set whether feed items should be sorted into reverse chronological order * - * @access public * @param bool $enable Sort as reverse chronological order. */ - function enable_order_by_date($enable = true) + public function enable_order_by_date($enable = true) { $this->order_by_date = (bool) $enable; } /** - * Allows you to override the character encoding reported by the feed. + * Set the character encoding used to parse the feed * - * @access public - * @param string $encoding Character encoding. + * This overrides the encoding reported by the feed, however it will fall + * back to the normal encoding detection if the override fails + * + * @param string $encoding Character encoding */ - function set_input_encoding($encoding = false) + public function set_input_encoding($encoding = false) { if ($encoding) { @@ -1000,7 +871,6 @@ class SimplePie /** * Set how much feed autodiscovery to do * - * @access public * @see SIMPLEPIE_LOCATOR_NONE * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION @@ -1008,325 +878,168 @@ class SimplePie * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION * @see SIMPLEPIE_LOCATOR_REMOTE_BODY * @see SIMPLEPIE_LOCATOR_ALL - * @param int $level Feed Autodiscovery Level (level can be a - * combination of the above constants, see bitwise OR operator) + * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) */ - function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) + public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) { $this->autodiscovery = (int) $level; } /** - * Allows you to change which class SimplePie uses for caching. - * Useful when you are overloading or extending SimplePie's default classes. + * Get the class registry * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Use this to override SimplePie's default classes + * @see SimplePie_Registry + * @return SimplePie_Registry */ - function set_cache_class($class = 'SimplePie_Cache') + public function &get_registry() { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache')) - { - $this->cache_class = $class; - return true; - } - return false; + return $this->registry; } - /** - * Allows you to change which class SimplePie uses for auto-discovery. + /**#@+ * Useful when you are overloading or extending SimplePie's default classes. * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @deprecated Use {@see get_registry()} instead * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * @param string $class Name of custom class + * @return boolean True on success, false otherwise + */ + /** + * Set which class SimplePie uses for caching */ - function set_locator_class($class = 'SimplePie_Locator') + public function set_cache_class($class = 'SimplePie_Cache') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator')) - { - $this->locator_class = $class; - return true; - } - return false; + return $this->registry->register('Cache', $class, true); } /** - * Allows you to change which class SimplePie uses for XML parsing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for auto-discovery */ - function set_parser_class($class = 'SimplePie_Parser') + public function set_locator_class($class = 'SimplePie_Locator') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser')) - { - $this->parser_class = $class; - return true; - } - return false; + return $this->registry->register('Locator', $class, true); } /** - * Allows you to change which class SimplePie uses for remote file fetching. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for XML parsing */ - function set_file_class($class = 'SimplePie_File') + public function set_parser_class($class = 'SimplePie_Parser') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File')) - { - $this->file_class = $class; - return true; - } - return false; + return $this->registry->register('Parser', $class, true); } /** - * Allows you to change which class SimplePie uses for data sanitization. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for remote file fetching */ - function set_sanitize_class($class = 'SimplePie_Sanitize') + public function set_file_class($class = 'SimplePie_File') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize')) - { - $this->sanitize = new $class; - return true; - } - return false; + return $this->registry->register('File', $class, true); } /** - * Allows you to change which class SimplePie uses for handling feed items. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for data sanitization */ - function set_item_class($class = 'SimplePie_Item') + public function set_sanitize_class($class = 'SimplePie_Sanitize') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item')) - { - $this->item_class = $class; - return true; - } - return false; + return $this->registry->register('Sanitize', $class, true); } /** - * Allows you to change which class SimplePie uses for handling author data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for handling feed items */ - function set_author_class($class = 'SimplePie_Author') + public function set_item_class($class = 'SimplePie_Item') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author')) - { - $this->author_class = $class; - return true; - } - return false; + return $this->registry->register('Item', $class, true); } /** - * Allows you to change which class SimplePie uses for handling category data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for handling author data */ - function set_category_class($class = 'SimplePie_Category') + public function set_author_class($class = 'SimplePie_Author') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category')) - { - $this->category_class = $class; - return true; - } - return false; + return $this->registry->register('Author', $class, true); } /** - * Allows you to change which class SimplePie uses for feed enclosures. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for handling category data */ - function set_enclosure_class($class = 'SimplePie_Enclosure') + public function set_category_class($class = 'SimplePie_Category') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure')) - { - $this->enclosure_class = $class; - return true; - } - return false; + return $this->registry->register('Category', $class, true); } /** - * Allows you to change which class SimplePie uses for captions - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for feed enclosures */ - function set_caption_class($class = 'SimplePie_Caption') + public function set_enclosure_class($class = 'SimplePie_Enclosure') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption')) - { - $this->caption_class = $class; - return true; - } - return false; + return $this->registry->register('Enclosure', $class, true); } /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for `` captions */ - function set_copyright_class($class = 'SimplePie_Copyright') + public function set_caption_class($class = 'SimplePie_Caption') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright')) - { - $this->copyright_class = $class; - return true; - } - return false; + return $this->registry->register('Caption', $class, true); } /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for `` */ - function set_credit_class($class = 'SimplePie_Credit') + public function set_copyright_class($class = 'SimplePie_Copyright') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit')) - { - $this->credit_class = $class; - return true; - } - return false; + return $this->registry->register('Copyright', $class, true); } /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for `` */ - function set_rating_class($class = 'SimplePie_Rating') + public function set_credit_class($class = 'SimplePie_Credit') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating')) - { - $this->rating_class = $class; - return true; - } - return false; + return $this->registry->register('Credit', $class, true); } /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for `` */ - function set_restriction_class($class = 'SimplePie_Restriction') + public function set_rating_class($class = 'SimplePie_Rating') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction')) - { - $this->restriction_class = $class; - return true; - } - return false; + return $this->registry->register('Rating', $class, true); } /** - * Allows you to change which class SimplePie uses for content-type sniffing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for `` */ - function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') + public function set_restriction_class($class = 'SimplePie_Restriction') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer')) - { - $this->content_type_sniffer_class = $class; - return true; - } - return false; + return $this->registry->register('Restriction', $class, true); } /** - * Allows you to change which class SimplePie uses item sources. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * Set which class SimplePie uses for content-type sniffing */ - function set_source_class($class = 'SimplePie_Source') + public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source')) - { - $this->source_class = $class; - return true; - } - return false; + return $this->registry->register('Content_Type_Sniffer', $class, true); + } + + /** + * Set which class SimplePie uses item sources + */ + public function set_source_class($class = 'SimplePie_Source') + { + return $this->registry->register('Source', $class, true); } + /**#@-*/ /** - * Allows you to override the default user agent string. + * Set the user agent string * - * @access public * @param string $ua New user agent string. */ - function set_useragent($ua = SIMPLEPIE_USERAGENT) + public function set_useragent($ua = SIMPLEPIE_USERAGENT) { $this->useragent = (string) $ua; } @@ -1334,10 +1047,9 @@ class SimplePie /** * Set callback function to create cache filename with * - * @access public * @param mixed $function Callback function */ - function set_cache_name_function($function = 'md5') + public function set_cache_name_function($function = 'md5') { if (is_callable($function)) { @@ -1346,31 +1058,14 @@ class SimplePie } /** - * Set javascript query string parameter + * Set options to make SP as fast as possible * - * @access public - * @param mixed $get Javascript query string parameter - */ - function set_javascript($get = 'js') - { - if ($get) - { - $this->javascript = (string) $get; - } - else - { - $this->javascript = false; - } - } - - /** - * Set options to make SP as fast as possible. Forgoes a - * substantial amount of data sanitization in favor of speed. + * Forgoes a substantial amount of data sanitization in favor of speed. This + * turns SimplePie into a dumb parser of feeds. * - * @access public * @param bool $set Whether to set them or not */ - function set_stupidly_fast($set = false) + public function set_stupidly_fast($set = false) { if ($set) { @@ -1386,20 +1081,19 @@ class SimplePie /** * Set maximum number of feeds to check with autodiscovery * - * @access public * @param int $max Maximum number of feeds to check */ - function set_max_checked_feeds($max = 10) + public function set_max_checked_feeds($max = 10) { $this->max_checked_feeds = (int) $max; } - function remove_div($enable = true) + public function remove_div($enable = true) { $this->sanitize->remove_div($enable); } - function strip_htmltags($tags = '', $encode = null) + public function strip_htmltags($tags = '', $encode = null) { if ($tags === '') { @@ -1412,12 +1106,12 @@ class SimplePie } } - function encode_instead_of_strip($enable = true) + public function encode_instead_of_strip($enable = true) { $this->sanitize->encode_instead_of_strip($enable); } - function strip_attributes($attribs = '') + public function strip_attributes($attribs = '') { if ($attribs === '') { @@ -1426,12 +1120,34 @@ class SimplePie $this->sanitize->strip_attributes($attribs); } - function set_output_encoding($encoding = 'UTF-8') + /** + * Set the output encoding + * + * Allows you to override SimplePie's output to match that of your webpage. + * This is useful for times when your webpages are not being served as + * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and + * is similar to {@see set_input_encoding()}. + * + * It should be noted, however, that not all character encodings can support + * all characters. If your page is being served as ISO-8859-1 and you try + * to display a Japanese feed, you'll likely see garbled characters. + * Because of this, it is highly recommended to ensure that your webpages + * are served as UTF-8. + * + * The number of supported character encodings depends on whether your web + * host supports {@link http://php.net/mbstring mbstring}, + * {@link http://php.net/iconv iconv}, or both. See + * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for + * more information. + * + * @param string $encoding + */ + public function set_output_encoding($encoding = 'UTF-8') { $this->sanitize->set_output_encoding($encoding); } - function strip_comments($strip = false) + public function strip_comments($strip = false) { $this->sanitize->strip_comments($strip); } @@ -1440,42 +1156,25 @@ class SimplePie * Set element/attribute key/value pairs of HTML attributes * containing URLs that need to be resolved relative to the feed * - * @access public + * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, + * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, + * |q|@cite + * * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs + * @param array|null $element_attribute Element/attribute key/value pairs, null for default */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + public function set_url_replacements($element_attribute = null) { $this->sanitize->set_url_replacements($element_attribute); } - /** - * Set the handler to enable the display of cached favicons. - * - * @access public - * @param str $page Web-accessible path to the handler_favicon.php file. - * @param str $qs The query string that the value should be passed to. - */ - function set_favicon_handler($page = false, $qs = 'i') - { - if ($page !== false) - { - $this->favicon_handler = $page . '?' . $qs . '='; - } - else - { - $this->favicon_handler = ''; - } - } - /** * Set the handler to enable the display of cached images. * - * @access public * @param str $page Web-accessible path to the handler_image.php file. * @param str $qs The query string that the value should be passed to. */ - function set_image_handler($page = false, $qs = 'i') + public function set_image_handler($page = false, $qs = 'i') { if ($page !== false) { @@ -1488,20 +1187,28 @@ class SimplePie } /** - * Set the limit for items returned per-feed with multifeeds. + * Set the limit for items returned per-feed with multifeeds * - * @access public * @param integer $limit The maximum number of items to return. */ - function set_item_limit($limit = 0) + public function set_item_limit($limit = 0) { $this->item_limit = (int) $limit; } - function init() + /** + * Initialize the feed object + * + * This is what makes everything happen. Period. This is where all of the + * configuration options get processed, feeds are fetched, cached, and + * parsed, and all of that other good stuff. + * + * @return boolean True if successful, false otherwise + */ + public function init() { // Check absolute bare minimum requirements. - if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre')) + if (!extension_loaded('xml') || !extension_loaded('pcre')) { return false; } @@ -1522,331 +1229,383 @@ class SimplePie } } - if (isset($_GET[$this->javascript])) + if (method_exists($this->sanitize, 'set_registry')) { - SimplePie_Misc::output_javascript(); - exit; + $this->sanitize->set_registry($this->registry); } // Pass whatever was set with config options over to the sanitizer. - $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class); - $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); + // Pass the classes in for legacy support; new classes should use the registry instead + $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); + $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen); - if ($this->feed_url !== null || $this->raw_data !== null) + if (!empty($this->multifeed_url)) { - $this->data = array(); + $i = 0; + $success = 0; $this->multifeed_objects = array(); - $cache = false; - - if ($this->feed_url !== null) + $this->error = array(); + foreach ($this->multifeed_url as $url) { - $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url); - // Decide whether to enable caching - if ($this->cache && $parsed_feed_url['scheme'] !== '') - { - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'); - } - // If it's enabled and we don't want an XML dump, use the cache - if ($cache && !$this->xml_dump) - { - // Load the Cache - $this->data = $cache->load(); - if (!empty($this->data)) - { - // If the cache is for an outdated build of SimplePie - if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) - { - $cache->unlink(); - $this->data = array(); - } - // If we've hit a collision just rerun it with caching disabled - elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) - { - $cache = false; - $this->data = array(); - } - // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. - elseif (isset($this->data['feed_url'])) - { - // If the autodiscovery cache is still valid use it. - if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) - { - // Do not need to do feed autodiscovery yet. - if ($this->data['feed_url'] === $this->data['url']) - { - $cache->unlink(); - $this->data = array(); - } - else - { - $this->set_feed_url($this->data['feed_url']); - return $this->init(); - } - } - } - // Check if the cache has been updated - elseif ($cache->mtime() + $this->cache_duration < time()) - { - // If we have last-modified and/or etag set - if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) - { - $headers = array(); - if (isset($this->data['headers']['last-modified'])) - { - $headers['if-modified-since'] = $this->data['headers']['last-modified']; - } - if (isset($this->data['headers']['etag'])) - { - $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"'; - } - $file = new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen); - if ($file->success) - { - if ($file->status_code === 304) - { - $cache->touch(); - return true; - } - else - { - $headers = $file->headers; - } - } - else - { - unset($file); - } - } - } - // If the cache is still valid, just return true - else - { - return true; - } - } - // If the cache is empty, delete it - else - { - $cache->unlink(); - $this->data = array(); - } - } - // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. - if (!isset($file)) - { - if (is_a($this->file, 'SimplePie_File') && $this->file->url === $this->feed_url) - { - $file =& $this->file; - } - else - { - $file = new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen); - } - } - // If the file connection has an error, set SimplePie::error to that and quit - if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) + $this->multifeed_objects[$i] = clone $this; + $this->multifeed_objects[$i]->set_feed_url($url); + $single_success = $this->multifeed_objects[$i]->init(); + $success |= $single_success; + if (!$single_success) { - $this->error = $file->error; - if (!empty($this->data)) - { - return true; - } - else - { - return false; - } + $this->error[$i] = $this->multifeed_objects[$i]->error(); } + $i++; + } + return (bool) $success; + } + elseif ($this->feed_url === null && $this->raw_data === null) + { + return false; + } - if (!$this->force_feed) - { - // Check if the supplied URL is a feed, if it isn't, look for it. - $locate = new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class); - if (!$locate->is_feed($file)) - { - // We need to unset this so that if SimplePie::set_file() has been called that object is untouched - unset($file); - if ($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)) - { - if ($cache) - { - $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); - if (!$cache->save($this)) - { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - } - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'); - } - $this->feed_url = $file->url; - } - else - { - $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed."; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - } - $locate = null; - } + $this->error = null; + $this->data = array(); + $this->multifeed_objects = array(); + $cache = false; + + if ($this->feed_url !== null) + { + $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); - $headers = $file->headers; - $data = $file->body; - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); + // Decide whether to enable caching + if ($this->cache && $parsed_feed_url['scheme'] !== '') + { + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc')); } - else + + // Fetch the data via SimplePie_File into $this->raw_data + if (($fetched = $this->fetch_data($cache)) === true) { - $data = $this->raw_data; + return true; } + elseif ($fetched === false) { + return false; + } + + list($headers, $sniffed) = $fetched; + } + + // Set up array of possible encodings + $encodings = array(); + + // First check to see if input has been overridden. + if ($this->input_encoding !== false) + { + $encodings[] = $this->input_encoding; + } - // Set up array of possible encodings - $encodings = array(); + $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); + $text_types = array('text/xml', 'text/xml-external-parsed-entity'); - // First check to see if input has been overridden. - if ($this->input_encoding !== false) + // RFC 3023 (only applies to sniffed content) + if (isset($sniffed)) + { + if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = strtoupper($charset[1]); + } + $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); + $encodings[] = 'UTF-8'; + } + elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = $charset[1]; + } + $encodings[] = 'US-ASCII'; + } + // Text MIME-type default + elseif (substr($sniffed, 0, 5) === 'text/') { - $encodings[] = $this->input_encoding; + $encodings[] = 'US-ASCII'; } + } - $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); - $text_types = array('text/xml', 'text/xml-external-parsed-entity'); + // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 + $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); + $encodings[] = 'UTF-8'; + $encodings[] = 'ISO-8859-1'; - // RFC 3023 (only applies to sniffed content) - if (isset($sniffed)) + // There's no point in trying an encoding twice + $encodings = array_unique($encodings); + + // Loop through each possible encoding, till we return something, or run out of possibilities + foreach ($encodings as $encoding) + { + // Change the encoding to UTF-8 (as we always use UTF-8 internally) + if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) { - if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') + // Create new parser + $parser = $this->registry->create('Parser'); + + // If it's parsed fine + if ($parser->parse($utf8_data, 'UTF-8')) { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + $this->data = $parser->get_data(); + if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) { - $encodings[] = strtoupper($charset[1]); + $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + return false; } - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - } - elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') - { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + + if (isset($headers)) { - $encodings[] = $charset[1]; + $this->data['headers'] = $headers; } - $encodings[] = 'US-ASCII'; - } - // Text MIME-type default - elseif (substr($sniffed, 0, 5) === 'text/') - { - $encodings[] = 'US-ASCII'; + $this->data['build'] = SIMPLEPIE_BUILD; + + // Cache the file if caching is enabled + if ($cache && !$cache->save($this)) + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + return true; } } + } - // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - $encodings[] = 'ISO-8859-1'; + if (isset($parser)) + { + // We have an error, just set SimplePie_Misc::error to it and quit + $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + } + else + { + $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; + } - // There's no point in trying an encoding twice - $encodings = array_unique($encodings); + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); - // If we want the XML, just output that with the most likely encoding and quit - if ($this->xml_dump) - { - header('Content-type: text/xml; charset=' . $encodings[0]); - echo $data; - exit; - } + return false; + } - // Loop through each possible encoding, till we return something, or run out of possibilities - foreach ($encodings as $encoding) + /** + * Fetch the data via SimplePie_File + * + * If the data is already cached, attempt to fetch it from there instead + * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache + * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type + */ + protected function fetch_data(&$cache) + { + // If it's enabled, use the cache + if ($cache) + { + // Load the Cache + $this->data = $cache->load(); + if (!empty($this->data)) { - // Change the encoding to UTF-8 (as we always use UTF-8 internally) - if ($utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8')) + // If the cache is for an outdated build of SimplePie + if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) + { + $cache->unlink(); + $this->data = array(); + } + // If we've hit a collision just rerun it with caching disabled + elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) { - // Create new parser - $parser = new $this->parser_class(); + $cache = false; + $this->data = array(); + } + // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. + elseif (isset($this->data['feed_url'])) + { + // If the autodiscovery cache is still valid use it. + if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) + { + // Do not need to do feed autodiscovery yet. + if ($this->data['feed_url'] !== $this->data['url']) + { + $this->set_feed_url($this->data['feed_url']); + return $this->init(); + } - // If it's parsed fine - if ($parser->parse($utf8_data, 'UTF-8')) + $cache->unlink(); + $this->data = array(); + } + } + // Check if the cache has been updated + elseif ($cache->mtime() + $this->cache_duration < time()) + { + // If we have last-modified and/or etag set + if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) { - $this->data = $parser->get_data(); - if ($this->get_type() & ~SIMPLEPIE_TYPE_NONE) + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + if (isset($this->data['headers']['last-modified'])) { - if (isset($headers)) - { - $this->data['headers'] = $headers; - } - $this->data['build'] = SIMPLEPIE_BUILD; + $headers['if-modified-since'] = $this->data['headers']['last-modified']; + } + if (isset($this->data['headers']['etag'])) + { + $headers['if-none-match'] = $this->data['headers']['etag']; + } + + $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen)); - // Cache the file if caching is enabled - if ($cache && !$cache->save($this)) + if ($file->success) + { + if ($file->status_code === 304) { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + $cache->touch(); + return true; } - return true; } else { - $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; + unset($file); } } } + // If the cache is still valid, just return true + else + { + $this->raw_data = false; + return true; + } + } + // If the cache is empty, delete it + else + { + $cache->unlink(); + $this->data = array(); } - if (isset($parser)) + } + // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. + if (!isset($file)) + { + if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url) { - // We have an error, just set SimplePie_Misc::error to it and quit - $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + $file =& $this->file; } else { - $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); } - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; } - elseif (!empty($this->multifeed_url)) + // If the file connection has an error, set SimplePie::error to that and quit + if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) { - $i = 0; - $success = 0; - $this->multifeed_objects = array(); - foreach ($this->multifeed_url as $url) + $this->error = $file->error; + return !empty($this->data); + } + + if (!$this->force_feed) + { + // Check if the supplied URL is a feed, if it isn't, look for it. + $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds)); + + if (!$locate->is_feed($file)) { - if (SIMPLEPIE_PHP5) + // We need to unset this so that if SimplePie::set_file() has been called that object is untouched + unset($file); + try { - // This keyword needs to defy coding standards for PHP4 compatibility - $this->multifeed_objects[$i] = clone($this); + if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))) + { + $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed."; + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + return false; + } } - else + catch (SimplePie_Exception $e) + { + // This is usually because DOMDocument doesn't exist + $this->error = $e->getMessage(); + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); + return false; + } + if ($cache) { - $this->multifeed_objects[$i] = $this; + $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); + if (!$cache->save($this)) + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); } - $this->multifeed_objects[$i]->set_feed_url($url); - $success |= $this->multifeed_objects[$i]->init(); - $i++; + $this->feed_url = $file->url; } - return (bool) $success; - } - else - { - return false; + $locate = null; } + + $this->raw_data = $file->body; + + $headers = $file->headers; + $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); + $sniffed = $sniffer->get_type(); + + return array($headers, $sniffed); } /** - * Return the error message for the occured error + * Get the error message for the occured error * - * @access public - * @return string Error message + * @return string|array Error message, or array of messages for multifeeds */ - function error() + public function error() { return $this->error; } - function get_encoding() + /** + * Get the raw XML + * + * This is the same as the old `$feed->enable_xml_dump(true)`, but returns + * the data instead of printing it. + * + * @return string|boolean Raw XML data, false if the cache is used + */ + public function get_raw_data() { - return $this->sanitize->output_encoding; + return $this->raw_data; } - function handle_content_type($mime = 'text/html') + /** + * Get the character encoding used for output + * + * @since Preview Release + * @return string + */ + public function get_encoding() + { + return $this->sanitize->output_encoding; + } + + /** + * Send the content-type header with correct encoding + * + * This method ensures that the SimplePie-enabled page is being served with + * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} + * and character encoding HTTP headers (character encoding determined by the + * {@see set_output_encoding} config option). + * + * This won't work properly if any content or whitespace has already been + * sent to the browser, because it relies on PHP's + * {@link http://php.net/header header()} function, and these are the + * circumstances under which the function works. + * + * Because it's setting these settings for the entire page (as is the nature + * of HTTP headers), this should only be used once per page (again, at the + * top). + * + * @param string $mime MIME type to serve the page as + */ + public function handle_content_type($mime = 'text/html') { if (!headers_sent()) { @@ -1863,7 +1622,33 @@ class SimplePie } } - function get_type() + /** + * Get the type of the feed + * + * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against + * using {@link http://php.net/language.operators.bitwise bitwise operators} + * + * @since 0.8 (usage changed to using constants in 1.0) + * @see SIMPLEPIE_TYPE_NONE Unknown. + * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90. + * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). + * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland). + * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91. + * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92. + * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93. + * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94. + * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0. + * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x. + * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS. + * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). + * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS. + * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3. + * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0. + * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom. + * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type. + * @return int SIMPLEPIE_TYPE_* constant + */ + public function get_type() { if (!isset($this->data['type'])) { @@ -1944,72 +1729,18 @@ class SimplePie } /** - * Returns the URL for the favicon of the feed's website. + * Get the URL for the feed * - * @todo Cache atom:icon - * @access public - * @since 1.0 - */ - function get_favicon() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url)) - { - $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url); - - if ($this->cache && $this->favicon_handler) - { - $favicon_filename = call_user_func($this->cache_name_function, $favicon); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi'); - - if ($cache->load()) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $file = new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - - if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0) - { - $sniffer = new $this->content_type_sniffer_class($file); - if (substr($sniffer->get_type(), 0, 6) === 'image/') - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - trigger_error("$cache->name is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - // not an image - else - { - return false; - } - } - } - } - else - { - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - return false; - } - - /** + * May or may not be different from the URL passed to {@see set_feed_url()}, + * depending on whether auto-discovery was used. + * + * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) * @todo If we have a perm redirect we should return the new URL * @todo When we make the above change, let's support as well * @todo Also, |atom:link|@rel=self + * @return string|null */ - function subscribe_url() + public function subscribe_url() { if ($this->feed_url !== null) { @@ -2021,156 +1752,38 @@ class SimplePie } } - function subscribe_feed() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_outlook() - { - if ($this->feed_url !== null) - { - return $this->sanitize('outlook' . SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_podcast() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_itunes() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - /** - * Creates the subscribe_* methods' return data + * Get data for an feed-level element * - * @access private - * @param string $feed_url String to prefix to the feed URL - * @param string $site_url String to prefix to the site URL (and - * suffix to the feed URL) - * @return mixed URL if feed exists, false otherwise + * This method allows you to get access to ANY element/attribute that is a + * sub-element of the opening feed tag. + * + * The return value is an indexed array of elements matching the given + * namespace and tag name. Each element has `attribs`, `data` and `child` + * subkeys. For `attribs` and `child`, these contain namespace subkeys. + * `attribs` then has one level of associative name => value data (where + * `value` is a string) after the namespace. `child` has tag-indexed keys + * after the namespace, each member of which is an indexed array matching + * this same format. + * + * For example: + *
+	 * // This is probably a bad example because we already support
+	 * //  natively, but it shows you how to parse through
+	 * // the nodes.
+	 * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
+	 * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
+	 * $file = $content[0]['attribs']['']['url'];
+	 * echo $file;
+	 * 
+ * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array */ - function subscribe_service($feed_url, $site_url = null) - { - if ($this->subscribe_url()) - { - $return = $feed_url . rawurlencode($this->feed_url); - if ($site_url !== null && $this->get_link() !== null) - { - $return .= $site_url . rawurlencode($this->get_link()); - } - return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_aol() - { - return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url='); - } - - function subscribe_bloglines() - { - return $this->subscribe_service('http://www.bloglines.com/sub/'); - } - - function subscribe_eskobo() - { - return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage='); - } - - function subscribe_feedfeeds() - { - return $this->subscribe_service('http://www.feedfeeds.com/add?feed='); - } - - function subscribe_feedster() - { - return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl='); - } - - function subscribe_google() - { - return $this->subscribe_service('http://fusion.google.com/add?feedurl='); - } - - function subscribe_gritwire() - { - return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl='); - } - - function subscribe_msn() - { - return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru='); - } - - function subscribe_netvibes() - { - return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url='); - } - - function subscribe_newsburst() - { - return $this->subscribe_service('http://www.newsburst.com/Source/?add='); - } - - function subscribe_newsgator() - { - return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url='); - } - - function subscribe_odeo() - { - return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed='); - } - - function subscribe_podnova() - { - return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url='); - } - - function subscribe_rojo() - { - return $this->subscribe_service('http://www.rojo.com/add-subscription?resource='); - } - - function subscribe_yahoo() - { - return $this->subscribe_service('http://add.my.yahoo.com/rss?url='); - } - - function get_feed_tags($namespace, $tag) + public function get_feed_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_ATOM_10) @@ -2204,7 +1817,21 @@ class SimplePie return null; } - function get_channel_tags($namespace, $tag) + /** + * Get data for an channel-level element + * + * This method allows you to get access to ANY element/attribute in the + * channel/header section of the feed. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_channel_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_ATOM_ALL) @@ -2247,7 +1874,21 @@ class SimplePie return null; } - function get_image_tags($namespace, $tag) + /** + * Get data for an channel-level element + * + * This method allows you to get access to ANY element/attribute in the + * image/logo section of the feed. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_image_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_RSS_10) @@ -2283,7 +1924,19 @@ class SimplePie return null; } - function get_base($element = array()) + /** + * Get the base URL value from the feed + * + * Uses `` if available, otherwise uses the first link in the + * feed, or failing that, the URL of the feed itself. + * + * @see get_link + * @see subscribe_url + * + * @param array $element + * @return string + */ + public function get_base($element = array()) { if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) { @@ -2299,20 +1952,38 @@ class SimplePie } } - function sanitize($data, $type, $base = '') + /** + * Sanitize feed data + * + * @access private + * @see SimplePie_Sanitize::sanitize() + * @param string $data Data to sanitize + * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants + * @param string $base Base URL to resolve URLs against + * @return string Sanitized data + */ + public function sanitize($data, $type, $base = '') { return $this->sanitize->sanitize($data, $type, $base); } - function get_title() + /** + * Get the title of the feed + * + * Uses ``, `` or `<dc:title>` + * + * @since 1.0 (previously called `get_feed_title` since 0.8) + * @return string|null + */ + public function get_title() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { @@ -2340,7 +2011,14 @@ class SimplePie } } - function get_category($key = 0) + /** + * Get a category for the feed + * + * @since Unknown + * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Category|null + */ + public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) @@ -2353,7 +2031,15 @@ class SimplePie } } - function get_categories() + /** + * Get all categories for the feed + * + * Uses `<atom:category>`, `<category>` or `<dc:subject>` + * + * @since Unknown + * @return array|null List of {@see SimplePie_Category} objects + */ + public function get_categories() { $categories = array(); @@ -2374,7 +2060,7 @@ class SimplePie { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } - $categories[] = new $this->category_class($term, $scheme, $label); + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) { @@ -2389,20 +2075,20 @@ class SimplePie { $scheme = null; } - $categories[] = new $this->category_class($term, $scheme, null); + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) { - $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) { - $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($categories)) { - return SimplePie_Misc::array_unique($categories); + return array_unique($categories); } else { @@ -2410,7 +2096,14 @@ class SimplePie } } - function get_author($key = 0) + /** + * Get an author for the feed + * + * @since 1.1 + * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_author($key = 0) { $authors = $this->get_authors(); if (isset($authors[$key])) @@ -2423,7 +2116,15 @@ class SimplePie } } - function get_authors() + /** + * Get all authors for the feed + * + * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_authors() { $authors = array(); foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) @@ -2445,7 +2146,7 @@ class SimplePie } if ($name !== null || $email !== null || $uri !== null) { - $authors[] = new $this->author_class($name, $uri, $email); + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); } } if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) @@ -2467,25 +2168,25 @@ class SimplePie } if ($name !== null || $email !== null || $url !== null) { - $authors[] = new $this->author_class($name, $url, $email); + $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($authors)) { - return SimplePie_Misc::array_unique($authors); + return array_unique($authors); } else { @@ -2493,7 +2194,14 @@ class SimplePie } } - function get_contributor($key = 0) + /** + * Get a contributor for the feed + * + * @since 1.1 + * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_contributor($key = 0) { $contributors = $this->get_contributors(); if (isset($contributors[$key])) @@ -2506,7 +2214,15 @@ class SimplePie } } - function get_contributors() + /** + * Get all contributors for the feed + * + * Uses `<atom:contributor>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_contributors() { $contributors = array(); foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) @@ -2528,7 +2244,7 @@ class SimplePie } if ($name !== null || $email !== null || $uri !== null) { - $contributors[] = new $this->author_class($name, $uri, $email); + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); } } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) @@ -2550,13 +2266,13 @@ class SimplePie } if ($name !== null || $email !== null || $url !== null) { - $contributors[] = new $this->author_class($name, $url, $email); + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); } } if (!empty($contributors)) { - return SimplePie_Misc::array_unique($contributors); + return array_unique($contributors); } else { @@ -2564,7 +2280,15 @@ class SimplePie } } - function get_link($key = 0, $rel = 'alternate') + /** + * Get a single link for the feed + * + * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) + * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 + * @param string $rel The relationship of the link to return + * @return string|null Link URL + */ + public function get_link($key = 0, $rel = 'alternate') { $links = $this->get_links($rel); if (isset($links[$key])) @@ -2578,14 +2302,31 @@ class SimplePie } /** - * Added for parity between the parent-level and the item/entry-level. + * Get the permalink for the item + * + * Returns the first link available with a relationship of "alternate". + * Identical to {@see get_link()} with key 0 + * + * @see get_link + * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) + * @internal Added for parity between the parent-level and the item/entry-level. + * @return string|null Link URL */ - function get_permalink() + public function get_permalink() { return $this->get_link(0); } - function get_links($rel = 'alternate') + /** + * Get all links for the feed + * + * Uses `<atom:link>` or `<link>` + * + * @since Beta 2 + * @param string $rel The relationship of links to return + * @return array|null Links found for the feed (strings) + */ + public function get_links($rel = 'alternate') { if (!isset($this->data['links'])) { @@ -2629,7 +2370,7 @@ class SimplePie $keys = array_keys($this->data['links']); foreach ($keys as $key) { - if (SimplePie_Misc::is_isegment_nz_nc($key)) + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) { @@ -2659,20 +2400,29 @@ class SimplePie } } - function get_all_discovered_feeds() + public function get_all_discovered_feeds() { return $this->all_discovered_feeds; } - function get_description() + /** + * Get the content for the item + * + * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, + * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` + * + * @since 1.0 (previously called `get_feed_description()` since 0.8) + * @return string|null + */ + public function get_description() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) { @@ -2708,15 +2458,23 @@ class SimplePie } } - function get_copyright() + /** + * Get the copyright info for the feed + * + * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` + * + * @since 1.0 (previously called `get_feed_copyright()` since 0.8) + * @return string|null + */ + public function get_copyright() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) { @@ -2736,7 +2494,15 @@ class SimplePie } } - function get_language() + /** + * Get the language for the feed + * + * Uses `<language>`, `<dc:language>`, or @xml_lang + * + * @since 1.0 (previously called `get_feed_language()` since 0.8) + * @return string|null + */ + public function get_language() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) { @@ -2772,9 +2538,21 @@ class SimplePie } } - function get_latitude() + /** + * Get the latitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:lat>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_latitude() { - + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) { return (float) $return[0]['data']; @@ -2789,7 +2567,19 @@ class SimplePie } } - function get_longitude() + /** + * Get the longitude coordinates for the feed + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_longitude() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) { @@ -2809,7 +2599,16 @@ class SimplePie } } - function get_image_title() + /** + * Get the feed logo's title + * + * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. + * + * Uses `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_title() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { @@ -2837,7 +2636,18 @@ class SimplePie } } - function get_image_url() + /** + * Get the feed logo's URL + * + * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to + * have a "feed logo" URL. This points directly to the image itself. + * + * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, + * `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_url() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) { @@ -2869,10 +2679,21 @@ class SimplePie } } - function get_image_link() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { + /** + * Get the feed logo's link + * + * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This + * points to a human-readable page that the image should link to. + * + * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, + * `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_link() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) @@ -2889,7 +2710,17 @@ class SimplePie } } - function get_image_width() + /** + * Get the feed logo's link + * + * RSS 2.0 feeds are allowed to have a "feed logo" width. + * + * Uses `<image><width>` or defaults to 88.0 if no width is specified and + * the feed is an RSS 2.0 feed. + * + * @return int|float|null + */ + public function get_image_width() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width')) { @@ -2905,7 +2736,17 @@ class SimplePie } } - function get_image_height() + /** + * Get the feed logo's height + * + * RSS 2.0 feeds are allowed to have a "feed logo" height. + * + * Uses `<image><height>` or defaults to 31.0 if no height is specified and + * the feed is an RSS 2.0 feed. + * + * @return int|float|null + */ + public function get_image_height() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height')) { @@ -2921,7 +2762,16 @@ class SimplePie } } - function get_item_quantity($max = 0) + /** + * Get the number of items in the feed + * + * This is well-suited for {@link http://php.net/for for()} loops with + * {@see get_item()} + * + * @param int $max Maximum value to return. 0 for no limit + * @return int Number of items in the feed + */ + public function get_item_quantity($max = 0) { $max = (int) $max; $qty = count($this->get_items()); @@ -2935,7 +2785,19 @@ class SimplePie } } - function get_item($key = 0) + /** + * Get a single item from the feed + * + * This is better suited for {@link http://php.net/for for()} loops, whereas + * {@see get_items()} is better suited for + * {@link http://php.net/foreach foreach()} loops. + * + * @see get_item_quantity() + * @since Beta 2 + * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Item|null + */ + public function get_item($key = 0) { $items = $this->get_items(); if (isset($items[$key])) @@ -2948,7 +2810,20 @@ class SimplePie } } - function get_items($start = 0, $end = 0) + /** + * Get all items from the feed + * + * This is better suited for {@link http://php.net/for for()} loops, whereas + * {@see get_items()} is better suited for + * {@link http://php.net/foreach foreach()} loops. + * + * @see get_item_quantity + * @since Beta 2 + * @param int $start Index to start at + * @param int $end Number of items to return. 0 for all items after `$start` + * @return array|null List of {@see SimplePie_Item} objects + */ + public function get_items($start = 0, $end = 0) { if (!isset($this->data['items'])) { @@ -2964,7 +2839,7 @@ class SimplePie $keys = array_keys($items); foreach ($keys as $key) { - $this->data['items'][] = new $this->item_class($this, $items[$key]); + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) @@ -2972,7 +2847,7 @@ class SimplePie $keys = array_keys($items); foreach ($keys as $key) { - $this->data['items'][] = new $this->item_class($this, $items[$key]); + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) @@ -2980,7 +2855,7 @@ class SimplePie $keys = array_keys($items); foreach ($keys as $key) { - $this->data['items'][] = new $this->item_class($this, $items[$key]); + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) @@ -2988,7 +2863,7 @@ class SimplePie $keys = array_keys($items); foreach ($keys as $key) { - $this->data['items'][] = new $this->item_class($this, $items[$key]); + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) @@ -2996,7 +2871,7 @@ class SimplePie $keys = array_keys($items); foreach ($keys as $key) { - $this->data['items'][] = new $this->item_class($this, $items[$key]); + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } } @@ -3022,7 +2897,7 @@ class SimplePie $this->data['ordered_items'] = $this->data['items']; if ($do_sort) { - usort($this->data['ordered_items'], array(&$this, 'sort_items')); + usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); } } $items = $this->data['ordered_items']; @@ -3049,24 +2924,98 @@ class SimplePie } /** - * @static + * Set the favicon handler + * + * @deprecated Use your own favicon handling instead + */ + public function set_favicon_handler($page = false, $qs = 'i') + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Favicon handling has been removed, please use your own handling', $level); + return false; + } + + /** + * Get the favicon for the current feed + * + * @deprecated Use your own favicon handling instead + */ + public function get_favicon() + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Favicon handling has been removed, please use your own handling', $level); + + if (($url = $this->get_link()) !== null) + { + return 'http://g.etfv.co/' . urlencode($url); + } + + return false; + } + + /** + * Magic method handler + * + * @param string $method Method name + * @param array $args Arguments to the method + * @return mixed + */ + public function __call($method, $args) + { + if (strpos($method, 'subscribe_') === 0) + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level); + return ''; + } + if ($method === 'enable_xml_dump') + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level); + return false; + } + + $class = get_class($this); + $trace = debug_backtrace(); + $file = $trace[0]['file']; + $line = $trace[0]['line']; + trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR); + } + + /** + * Sorting callback for items + * + * @access private + * @param SimplePie $a + * @param SimplePie $b + * @return boolean */ - function sort_items($a, $b) + public static function sort_items($a, $b) { return $a->get_date('U') <= $b->get_date('U'); } /** - * @static + * Merge items from several feeds into one + * + * If you're merging multiple feeds together, they need to all have dates + * for the items or else SimplePie will refuse to sort them. + * + * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings + * @param array $urls List of SimplePie feed objects to merge + * @param int $start Starting item + * @param int $end Number of items to return + * @param int $limit Maximum number of items per feed + * @return array */ - function merge_items($urls, $start = 0, $end = 0, $limit = 0) + public static function merge_items($urls, $start = 0, $end = 0, $limit = 0) { if (is_array($urls) && sizeof($urls) > 0) { $items = array(); foreach ($urls as $arg) { - if (is_a($arg, 'SimplePie')) + if ($arg instanceof SimplePie) { $items = array_merge($items, $arg->get_items(0, $limit)); } @@ -3088,7 +3037,7 @@ class SimplePie $item = null; if ($do_sort) { - usort($items, array('SimplePie', 'sort_items')); + usort($items, array(get_class($urls[0]), 'sort_items')); } if ($end === 0) @@ -3108,38 +3057,94 @@ class SimplePie } } -class SimplePie_Item +/** + * Manages all author-related data + * + * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()} + * + * This class can be overloaded with {@see SimplePie::set_author_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Author { - var $feed; - var $data = array(); + /** + * Author's name + * + * @var string + * @see get_name() + */ + var $name; + + /** + * Author's link + * + * @var string + * @see get_link() + */ + var $link; + + /** + * Author's email address + * + * @var string + * @see get_email() + */ + var $email; - function SimplePie_Item($feed, $data) + /** + * Constructor, used to input the data + * + * @param string $name + * @param string $link + * @param string $email + */ + public function __construct($name = null, $link = null, $email = null) { - $this->feed = $feed; - $this->data = $data; + $this->name = $name; + $this->link = $link; + $this->email = $email; } - function __toString() + /** + * String-ified version + * + * @return string + */ + public function __toString() { - return md5(serialize($this->data)); + // There is no $this->data here + return md5(serialize($this)); } /** - * Remove items that link back to this before destroying this object + * Author's name + * + * @return string|null */ - function __destruct() + public function get_name() { - if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) + if ($this->name !== null) { - unset($this->feed); + return $this->name; + } + else + { + return null; } } - function get_item_tags($namespace, $tag) + /** + * Author's link + * + * @return string|null + */ + public function get_link() { - if (isset($this->data['child'][$namespace][$tag])) + if ($this->link !== null) { - return $this->data['child'][$namespace][$tag]; + return $this->link; } else { @@ -3147,406 +3152,1032 @@ class SimplePie_Item } } - function get_base($element = array()) + /** + * Author's email address + * + * @return string|null + */ + public function get_email() { - return $this->feed->get_base($element); + if ($this->email !== null) + { + return $this->email; + } + else + { + return null; + } } +} - function sanitize($data, $type, $base = '') - { - return $this->feed->sanitize($data, $type, $base); - } +/** + * Base for cache objects + * + * Classes to be used with {@see SimplePie_Cache::register()} are expected + * to implement this interface. + * + * @package SimplePie + * @subpackage Caching + */ +interface SimplePie_Cache_Base +{ + /** + * Feed cache type + * + * @var string + */ + const TYPE_FEED = 'spc'; - function get_feed() - { - return $this->feed; - } + /** + * Image cache type + * + * @var string + */ + const TYPE_IMAGE = 'spi'; - function get_id($hash = false) + /** + * Create a new cache object + * + * @param string $location Location string (from SimplePie::$cache_location) + * @param string $name Unique ID for the cache + * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data + */ + public function __construct($location, $name, $type); + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data); + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load(); + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime(); + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch(); + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink(); +} + +/** + * Base class for database-based caches + * + * @package SimplePie + * @subpackage Caching + */ +abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base +{ + /** + * Helper for database conversion + * + * Converts a given {@see SimplePie} object into data to be stored + * + * @param SimplePie $data + * @return array First item is the serialized data for storage, second item is the unique ID for this item + */ + protected static function prepare_simplepie_object_for_cache($data) { - if (!$hash) + $items = $data->get_items(); + $items_by_id = array(); + + if (!empty($items)) { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) + foreach ($items as $item) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + $items_by_id[$item->get_id()] = $item; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) + + if (count($items_by_id) !== count($items)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + $items_by_id = array(); + foreach ($items as $item) + { + $items_by_id[$item->get_id(true)] = $item; + } } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + + if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; } - elseif (($return = $this->get_permalink()) !== null) + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) { - return $return; + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; } - elseif (($return = $this->get_title()) !== null) + else { - return $return; + $channel = null; } - } - if ($this->get_permalink() !== null || $this->get_title() !== null) - { - return md5($this->get_permalink() . $this->get_title()); - } - else - { - return md5(serialize($this->data)); - } - } - function get_title() - { - if (!isset($this->data['title'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + if ($channel !== null) { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); + } } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + if (isset($data->data['items'])) { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + unset($data->data['items']); } - else + if (isset($data->data['ordered_items'])) { - $this->data['title'] = null; + unset($data->data['ordered_items']); } } - return $this->data['title']; + return array(serialize($data->data), $items_by_id); + } +} + +/** + * Caches data to the filesystem + * + * @package SimplePie + * @subpackage Caching + */ +class SimplePie_Cache_File implements SimplePie_Cache_Base +{ + /** + * Location string + * + * @see SimplePie::$cache_location + * @var string + */ + protected $location; + + /** + * Filename + * + * @var string + */ + protected $filename; + + /** + * File extension + * + * @var string + */ + protected $extension; + + /** + * File path + * + * @var string + */ + protected $name; + + /** + * Create a new cache object + * + * @param string $location Location string (from SimplePie::$cache_location) + * @param string $name Unique ID for the cache + * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data + */ + public function __construct($location, $name, $type) + { + $this->location = $location; + $this->filename = $name; + $this->extension = $type; + $this->name = "$this->location/$this->filename.$this->extension"; } - function get_description($description_only = false) + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + if ($data instanceof SimplePie) + { + $data = $data->data; + } + + $data = serialize($data); + return (bool) file_put_contents($this->name, $data); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + return false; + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + if (file_exists($this->name) && is_readable($this->name)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return unserialize(file_get_contents($this->name)); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + if (file_exists($this->name)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return filemtime($this->name); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + return false; + } + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() + { + if (file_exists($this->name)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + return touch($this->name); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + return false; + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() + { + if (file_exists($this->name)) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return unlink($this->name); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + return false; + } +} + +/** + * Caches data to memcache + * + * Registered for URLs with the "memcache" protocol + * + * For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will + * connect to memcache on `localhost` on port 11211. All tables will be + * prefixed with `sp_` and data will expire after 3600 seconds + * + * @package SimplePie + * @subpackage Caching + * @uses Memcache + */ +class SimplePie_Cache_Memcache implements SimplePie_Cache_Base +{ + /** + * Memcache instance + * + * @var Memcache + */ + protected $cache; + + /** + * Options + * + * @var array + */ + protected $options; + + /** + * Cache name + * + * @var string + */ + protected $name; + + /** + * Create a new cache object + * + * @param string $location Location string (from SimplePie::$cache_location) + * @param string $name Unique ID for the cache + * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data + */ + public function __construct($location, $name, $type) + { + $this->options = array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'extras' => array( + 'timeout' => 3600, // one hour + 'prefix' => 'simplepie_', + ), + ); + $parsed = SimplePie_Cache::parse_URL($location); + $this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host']; + $this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port']; + $this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']); + $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); + + $this->cache = new Memcache(); + $this->cache->addServer($this->options['host'], (int) $this->options['port']); + } + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) + { + if ($data instanceof SimplePie) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML); + $data = $data->data; } + return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); + } - elseif (!$description_only) + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + $data = $this->cache->get($this->name); + + if ($data !== false) { - return $this->get_content(true); + return unserialize($data); } - else + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + $data = $this->cache->get($this->name); + + if ($data !== false) { - return null; + // essentially ignore the mtime because Memcache expires on it's own + return time(); } + + return false; } - function get_content($content_only = false) + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) + $data = $this->cache->get($this->name); + + if ($data !== false) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) + + return false; + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() + { + return $this->cache->delete($this->name, 0); + } +} + +/** + * Caches data to a MySQL database + * + * Registered for URLs with the "mysql" protocol + * + * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will + * connect to the `mydb` database on `localhost` on port 3306, with the user + * `root` and the password `password`. All tables will be prefixed with `sp_` + * + * @package SimplePie + * @subpackage Caching + */ +class SimplePie_Cache_MySQL extends SimplePie_Cache_DB +{ + /** + * PDO instance + * + * @var PDO + */ + protected $mysql; + + /** + * Options + * + * @var array + */ + protected $options; + + /** + * Cache ID + * + * @var string + */ + protected $id; + + /** + * Create a new cache object + * + * @param string $location Location string (from SimplePie::$cache_location) + * @param string $name Unique ID for the cache + * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data + */ + public function __construct($location, $name, $type) + { + $this->options = array( + 'user' => null, + 'pass' => null, + 'host' => '127.0.0.1', + 'port' => '3306', + 'path' => '', + 'extras' => array( + 'prefix' => '', + ), + ); + $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); + + // Path is prefixed with a "/" + $this->options['dbname'] = substr($this->options['path'], 1); + + try { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + catch (PDOException $e) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + $this->mysql = null; + return; } - elseif (!$content_only) + + $this->id = $name . $type; + + if (!$query = $this->mysql->query('SHOW TABLES')) { - return $this->get_description(true); + $this->mysql = null; + return; } - else + + $db = array(); + while ($row = $query->fetchColumn()) { - return null; + $db[] = $row; } - } - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) + if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) { - return $categories[$key]; + $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))'); + if ($query === false) + { + $this->mysql = null; + } } - else + + if (!in_array($this->options['extras']['prefix'] . 'items', $db)) { - return null; + $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'); + if ($query === false) + { + $this->mysql = null; + } } } - function get_categories() + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) { - $categories = array(); + if ($this->mysql === null) + { + return false; + } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + if ($data instanceof SimplePie) { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) + $data = clone $data; + + $prepared = self::prepare_simplepie_object_for_cache($data); + + $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); + $query->bindValue(':feed', $this->id); + if ($query->execute()) { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + if ($query->fetchColumn() > 0) + { + $items = count($prepared[1]); + if ($items) + { + $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed'; + $query = $this->mysql->prepare($sql); + $query->bindValue(':items', $items); + } + else + { + $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed'; + $query = $this->mysql->prepare($sql); + } + + $query->bindValue(':data', $prepared[0]); + $query->bindValue(':time', time()); + $query->bindValue(':feed', $this->id); + if (!$query->execute()) + { + return false; + } + } + else + { + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)'); + $query->bindValue(':feed', $this->id); + $query->bindValue(':count', count($prepared[1])); + $query->bindValue(':data', $prepared[0]); + $query->bindValue(':time', time()); + if (!$query->execute()) + { + return false; + } + } + + $ids = array_keys($prepared[1]); + if (!empty($ids)) + { + foreach ($ids as $id) + { + $database_ids[] = $this->mysql->quote($id); + } + + $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed'); + $query->bindValue(':feed', $this->id); + + if ($query->execute()) + { + $existing_ids = array(); + while ($row = $query->fetchColumn()) + { + $existing_ids[] = $row; + } + + $new_ids = array_diff($ids, $existing_ids); + + foreach ($new_ids as $new_id) + { + if (!($date = $prepared[1][$new_id]->get_date('U'))) + { + $date = time(); + } + + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)'); + $query->bindValue(':feed', $this->id); + $query->bindValue(':id', $new_id); + $query->bindValue(':data', serialize($prepared[1][$new_id]->data)); + $query->bindValue(':date', $date); + if (!$query->execute()) + { + return false; + } + } + return true; + } + } + else + { + return true; + } } - if (isset($category['attribs']['']['label'])) + } + else + { + $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); + $query->bindValue(':feed', $this->id); + if ($query->execute()) { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + if ($query->rowCount() > 0) + { + $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed'); + $query->bindValue(':data', serialize($data)); + $query->bindValue(':time', time()); + $query->bindValue(':feed', $this->id); + if ($this->execute()) + { + return true; + } + } + else + { + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)'); + $query->bindValue(':id', $this->id); + $query->bindValue(':data', serialize($data)); + $query->bindValue(':time', time()); + if ($query->execute()) + { + return true; + } + } } - $categories[] = new $this->feed->category_class($term, $scheme, $label); } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + return false; + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + if ($this->mysql === null) { - // This is really the label, but keep this as the term also for BC. - // Label will also work on retrieving because that falls back to term. - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - if (isset($category['attribs']['']['domain'])) + return false; + } + + $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + if ($query->execute() && ($row = $query->fetch())) + { + $data = unserialize($row[1]); + + if (isset($this->options['items'][0])) { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + $items = (int) $this->options['items'][0]; } else { - $scheme = null; + $items = (int) $row[0]; } - $categories[] = new $this->feed->category_class($term, $scheme, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + + if ($items !== 0) + { + if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]; + } + else + { + $feed = null; + } + + if ($feed !== null) + { + $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC'; + if ($items > 0) + { + $sql .= ' LIMIT ' . $items; + } + + $query = $this->mysql->prepare($sql); + $query->bindValue(':feed', $this->id); + if ($query->execute()) + { + while ($row = $query->fetchColumn()) + { + $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row); + } + } + else + { + return false; + } + } + } + return $data; } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + if ($this->mysql === null) { - $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return false; } - if (!empty($categories)) + $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + if ($query->execute() && ($time = $query->fetchColumn())) { - return SimplePie_Misc::array_unique($categories); + return $time; } else { - return null; + return false; } } - function get_author($key = 0) + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() { - $authors = $this->get_authors(); - if (isset($authors[$key])) + if ($this->mysql === null) { - return $authors[$key]; - } - else - { - return null; + return false; } - } - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) + $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id'); + $query->bindValue(':time', time()); + $query->bindValue(':id', $this->id); + if ($query->execute() && $query->rowCount() > 0) { - return $contributors[$key]; + return true; } else { - return null; + return false; } } - function get_contributors() + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() { - $contributors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] = new $this->feed->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + if ($this->mysql === null) { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] = new $this->feed->author_class($name, $url, $email); - } + return false; } - if (!empty($contributors)) + $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id'); + $query2->bindValue(':id', $this->id); + if ($query->execute() && $query2->execute()) { - return SimplePie_Misc::array_unique($contributors); + return true; } else { - return null; + return false; } } +} - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $authors[] = new $this->feed->author_class($name, $uri, $email); - } - } - if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] = new $this->feed->author_class($name, $url, $email); - } - } - if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) - { - $authors[] = new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } +/** + * Used to create cache objects + * + * This class can be overloaded with {@see SimplePie::set_cache_class()}, + * although the preferred way is to create your own handler + * via {@see register()} + * + * @package SimplePie + * @subpackage Caching + */ +class SimplePie_Cache +{ + /** + * Cache handler classes + * + * These receive 3 parameters to their constructor, as documented in + * {@see register()} + * @var array + */ + protected static $handlers = array( + 'mysql' => 'SimplePie_Cache_MySQL', + 'memcache' => 'SimplePie_Cache_Memcache', + ); - if (!empty($authors)) + /** + * Don't call the constructor. Please. + */ + private function __construct() { } + + /** + * Create a new SimplePie_Cache object + * + * @param string $location URL location (scheme is used to determine handler) + * @param string $filename Unique identifier for cache object + * @param string $extension 'spi' or 'spc' + * @return SimplePie_Cache_Base Type of object depends on scheme of `$location` + */ + public static function get_handler($location, $filename, $extension) + { + $type = explode(':', $location, 2); + $type = $type[0]; + if (!empty(self::$handlers[$type])) { - return SimplePie_Misc::array_unique($authors); + $class = self::$handlers[$type]; + return new $class($location, $filename, $extension); } - elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) + + return new SimplePie_Cache_File($location, $filename, $extension); + } + + /** + * Create a new SimplePie_Cache object + * + * @deprecated Use {@see get_handler} instead + */ + public function create($location, $filename, $extension) + { + trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED); + return self::get_handler($location, $filename, $extension); + } + + /** + * Register a handler + * + * @param string $type DSN type to register for + * @param string $class Name of handler class. Must implement SimplePie_Cache_Base + */ + public static function register($type, $class) + { + self::$handlers[$type] = $class; + } + + /** + * Parse a URL into an array + * + * @param string $url + * @return array + */ + public static function parse_URL($url) + { + $params = parse_url($url); + $params['extras'] = array(); + if (isset($params['query'])) { - return $authors; + parse_str($params['query'], $params['extras']); } - elseif ($authors = $this->feed->get_authors()) + return $params; + } +} + +/** + * Handles `<media:text>` captions as defined in Media RSS. + * + * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()} + * + * This class can be overloaded with {@see SimplePie::set_caption_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Caption +{ + /** + * Content type + * + * @var string + * @see get_type() + */ + var $type; + + /** + * Language + * + * @var string + * @see get_language() + */ + var $lang; + + /** + * Start time + * + * @var string + * @see get_starttime() + */ + var $startTime; + + /** + * End time + * + * @var string + * @see get_endtime() + */ + var $endTime; + + /** + * Caption text + * + * @var string + * @see get_text() + */ + var $text; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) + { + $this->type = $type; + $this->lang = $lang; + $this->startTime = $startTime; + $this->endTime = $endTime; + $this->text = $text; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the end time + * + * @return string|null Time in the format 'hh:mm:ss.SSS' + */ + public function get_endtime() + { + if ($this->endTime !== null) { - return $authors; + return $this->endTime; } else { @@ -3554,19 +4185,17 @@ class SimplePie_Item } } - function get_copyright() + /** + * Get the language + * + * @link http://tools.ietf.org/html/rfc3066 + * @return string|null Language code as per RFC 3066 + */ + public function get_language() { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + if ($this->lang !== null) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return $this->lang; } else { @@ -3574,67 +4203,16 @@ class SimplePie_Item } } - function get_date($date_format = 'j F Y, g:i a') + /** + * Get the start time + * + * @return string|null Time in the format 'hh:mm:ss.SSS' + */ + public function get_starttime() { - if (!isset($this->data['date'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - - if (!empty($this->data['date']['raw'])) - { - $parser = SimplePie_Parse_Date::get(); - $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); - } - else - { - $this->data['date'] = null; - } - } - if ($this->data['date']) + if ($this->startTime !== null) { - $date_format = (string) $date_format; - switch ($date_format) - { - case '': - return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); - - case 'U': - return $this->data['date']['parsed']; - - default: - return date($date_format, $this->data['date']['parsed']); - } + return $this->startTime; } else { @@ -3642,15 +4220,16 @@ class SimplePie_Item } } - function get_local_date($date_format = '%c') + /** + * Get the text of the caption + * + * @return string|null + */ + public function get_text() { - if (!$date_format) - { - return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (($date = $this->get_date('U')) !== null && $date !== false) + if ($this->text !== null) { - return strftime($date_format, $date); + return $this->text; } else { @@ -3658,30 +4237,95 @@ class SimplePie_Item } } - function get_permalink() + /** + * Get the content type (not MIME type) + * + * @return string|null Either 'text' or 'html' + */ + public function get_type() { - $link = $this->get_link(); - $enclosure = $this->get_enclosure(0); - if ($link !== null) - { - return $link; - } - elseif ($enclosure !== null) + if ($this->type !== null) { - return $enclosure->get_link(); + return $this->type; } else { return null; } } +} - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if ($links[$key] !== null) +/** + * Manages all category-related data + * + * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()} + * + * This class can be overloaded with {@see SimplePie::set_category_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Category +{ + /** + * Category identifier + * + * @var string + * @see get_term + */ + var $term; + + /** + * Categorization scheme identifier + * + * @var string + * @see get_scheme() + */ + var $scheme; + + /** + * Human readable label + * + * @var string + * @see get_label() + */ + var $label; + + /** + * Constructor, used to input the data + * + * @param string $term + * @param string $scheme + * @param string $label + */ + public function __construct($term = null, $scheme = null, $label = null) + { + $this->term = $term; + $this->scheme = $scheme; + $this->label = $label; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the category identifier + * + * @return string|null + */ + public function get_term() + { + if ($this->term !== null) { - return $links[$key]; + return $this->term; } else { @@ -3689,1895 +4333,1352 @@ class SimplePie_Item } } - function get_links($rel = 'alternate') + /** + * Get the categorization scheme identifier + * + * @return string|null + */ + public function get_scheme() { - if (!isset($this->data['links'])) + if ($this->scheme !== null) { - $this->data['links'] = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + return $this->scheme; + } + else + { + return null; + } + } - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + /** + * Get the human readable label + * + * @return string|null + */ + public function get_label() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return $this->get_term(); + } + } +} + +/** + * Content-type sniffing + * + * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06 + * + * This is used since we can't always trust Content-Type headers, and is based + * upon the HTML5 parsing rules. + * + * + * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()} + * + * @package SimplePie + * @subpackage HTTP + */ +class SimplePie_Content_Type_Sniffer +{ + /** + * File object + * + * @var SimplePie_File + */ + var $file; + + /** + * Create an instance of the class with the input file + * + * @param SimplePie_Content_Type_Sniffer $file Input file + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Get the Content-Type of the specified file + * + * @return string Actual Content-Type + */ + public function get_type() + { + if (isset($this->file->headers['content-type'])) + { + if (!isset($this->file->headers['content-encoding']) + && ($this->file->headers['content-type'] === 'text/plain' + || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } + return $this->text_or_binary(); } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + + if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + $official = substr($this->file->headers['content-type'], 0, $pos); } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + else { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + $official = $this->file->headers['content-type']; } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + $official = trim(strtolower($official)); + + if ($official === 'unknown/unknown' + || $official === 'application/unknown') { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + return $this->unknown(); } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + elseif (substr($official, -4) === '+xml' + || $official === 'text/xml' + || $official === 'application/xml') { - if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } + return $official; } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) + elseif (substr($official, 0, 6) === 'image/') { - if (SimplePie_Misc::is_isegment_nz_nc($key)) + if ($return = $this->image()) { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } + return $return; } - elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + else { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + return $official; } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); } - } - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; + elseif ($official === 'text/html') + { + return $this->feed_or_html(); + } + else + { + return $official; + } } else { - return null; + return $this->unknown(); } } /** - * @todo Add ability to prefer one type of content over another (in a media group). + * Sniff text or binary + * + * @return string Actual Content-Type */ - function get_enclosure($key = 0, $prefer = null) + public function text_or_binary() { - $enclosures = $this->get_enclosures(); - if (isset($enclosures[$key])) + if (substr($this->file->body, 0, 2) === "\xFE\xFF" + || substr($this->file->body, 0, 2) === "\xFF\xFE" + || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" + || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") { - return $enclosures[$key]; + return 'text/plain'; + } + elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) + { + return 'application/octect-stream'; } else { - return null; + return 'text/plain'; } } /** - * Grabs all available enclosures (podcasts, etc.) - * - * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. - * - * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support. + * Sniff unknown * - * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). - * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). + * @return string Actual Content-Type */ - function get_enclosures() + public function unknown() { - if (!isset($this->data['enclosures'])) + $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); + if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html' + || strtolower(substr($this->file->body, $ws, 5)) === '<html' + || strtolower(substr($this->file->body, $ws, 7)) === '<script') { - $this->data['enclosures'] = array(); - - // Elements - $captions_parent = null; - $categories_parent = null; - $copyrights_parent = null; - $credits_parent = null; - $description_parent = null; - $duration_parent = null; - $hashes_parent = null; - $keywords_parent = null; - $player_parent = null; - $ratings_parent = null; - $restrictions_parent = null; - $thumbnails_parent = null; - $title_parent = null; - - // Let's do the channel and item-level ones first, and just re-use them if we need to. - $parent = $this->get_feed(); - - // CAPTIONS - if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - if (is_array($captions_parent)) + return 'text/html'; + } + elseif (substr($this->file->body, 0, 5) === '%PDF-') + { + return 'application/pdf'; + } + elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') + { + return 'application/postscript'; + } + elseif (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") + { + return 'image/vnd.microsoft.icon'; + } + else + { + return $this->text_or_binary(); + } + } + + /** + * Sniff images + * + * @return string Actual Content-Type + */ + public function image() + { + if (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") + { + return 'image/vnd.microsoft.icon'; + } + else + { + return false; + } + } + + /** + * Sniff HTML + * + * @return string Actual Content-Type + */ + public function feed_or_html() + { + $len = strlen($this->file->body); + $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); + + while ($pos < $len) + { + switch ($this->file->body[$pos]) { - $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent)); + case "\x09": + case "\x0A": + case "\x0D": + case "\x20": + $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); + continue 2; + + case '<': + $pos++; + break; + + default: + return 'text/html'; } - // CATEGORIES - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + if (substr($this->file->body, $pos, 3) === '!--') { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) + $pos += 3; + if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + $pos += 3; } else { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + return 'text/html'; } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + elseif (substr($this->file->body, $pos, 1) === '!') { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) + if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + $pos++; } else { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + return 'text/html'; } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) + elseif (substr($this->file->body, $pos, 1) === '?') { - $term = null; - $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - $label = null; - if (isset($category['attribs']['']['text'])) + if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) { - $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + $pos += 2; } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - - if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) + else { - foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) - { - if (isset($subcategory['attribs']['']['text'])) - { - $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - } + return 'text/html'; } } - if (is_array($categories_parent)) + elseif (substr($this->file->body, $pos, 3) === 'rss' + || substr($this->file->body, $pos, 7) === 'rdf:RDF') { - $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent)); + return 'application/rss+xml'; } - - // COPYRIGHT - if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + elseif (substr($this->file->body, $pos, 4) === 'feed') { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label); + return 'application/atom+xml'; } - elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + else { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label); + return 'text/html'; } + } - // CREDITS - if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - if (is_array($credits_parent)) - { - $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent)); - } + return 'text/html'; + } +} - // DESCRIPTION - if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } +/** + * Manages `<media:copyright>` copyright tags as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_copyright()} + * + * This class can be overloaded with {@see SimplePie::set_copyright_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Copyright +{ + /** + * Copyright URL + * + * @var string + * @see get_url() + */ + var $url; - // DURATION - if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) - { - $seconds = null; - $minutes = null; - $hours = null; - if (isset($duration_parent[0]['data'])) - { - $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - if (sizeof($temp) > 0) - { - $seconds = (int) array_pop($temp); - } - if (sizeof($temp) > 0) - { - $minutes = (int) array_pop($temp); - $seconds += $minutes * 60; - } - if (sizeof($temp) > 0) - { - $hours = (int) array_pop($temp); - $seconds += $hours * 3600; - } - unset($temp); - $duration_parent = $seconds; - } - } + /** + * Attribution + * + * @var string + * @see get_attribution() + */ + var $label; - // HASHES - if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - if (is_array($hashes_parent)) - { - $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent)); - } + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($url = null, $label = null) + { + $this->url = $url; + $this->label = $label; + } - // KEYWORDS - if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - if (is_array($keywords_parent)) - { - $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent)); - } + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } - // PLAYER - if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } + /** + * Get the copyright URL + * + * @return string|null URL to copyright information + */ + public function get_url() + { + if ($this->url !== null) + { + return $this->url; + } + else + { + return null; + } + } - // RATINGS - if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - if (is_array($ratings_parent)) - { - $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent)); - } + /** + * Get the attribution text + * + * @return string|null + */ + public function get_attribution() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return null; + } + } +} - // RESTRICTIONS - if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - if (is_array($restrictions_parent)) - { - $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent)); - } +/** + * SimplePie class. + * + * Class for backward compatibility. + * + * @deprecated Use {@see SimplePie} directly + * @package SimplePie + * @subpackage API + */ +class SimplePie_Core extends SimplePie +{ - // THUMBNAILS - if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } +} - // TITLES - if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } +/** + * Handles `<media:credit>` as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()} + * + * This class can be overloaded with {@see SimplePie::set_credit_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Credit +{ + /** + * Credited role + * + * @var string + * @see get_role() + */ + var $role; - // Clear the memory - unset($parent); + /** + * Organizational scheme + * + * @var string + * @see get_scheme() + */ + var $scheme; - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + /** + * Credited name + * + * @var string + * @see get_name() + */ + var $name; - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($role = null, $scheme = null, $name = null) + { + $this->role = $role; + $this->scheme = $scheme; + $this->name = $name; + } - // If we have media:group tags, loop through them. - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) - { - if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) - { - // If we have media:content tags, loop through them. - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; + /** + * Get the role of the person receiving credit + * + * @return string|null + */ + public function get_role() + { + if ($this->role !== null) + { + return $this->role; + } + else + { + return null; + } + } - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + /** + * Get the organizational scheme + * + * @return string|null + */ + public function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + /** + * Get the credited person/entity's name + * + * @return string|null + */ + public function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } +} - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } +/** + * Decode HTML Entities + * + * This implements HTML5 as of revision 967 (2007-06-28) + * + * @deprecated Use DOMDocument instead! + * @package SimplePie + */ +class SimplePie_Decode_HTML_Entities +{ + /** + * Data to be parsed + * + * @access private + * @var string + */ + var $data = ''; - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } + /** + * Currently consumed bytes + * + * @access private + * @var string + */ + var $consumed = ''; - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } + /** + * Position of the current byte being parsed + * + * @access private + * @var int + */ + var $position = 0; - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + public function __construct($data) + { + $this->data = $data; + } - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } + /** + * Parse the input data + * + * @access public + * @return string Output data + */ + public function parse() + { + while (($this->position = strpos($this->data, '&', $this->position)) !== false) + { + $this->consume(); + $this->entity(); + $this->consumed = ''; + } + return $this->data; + } - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } + /** + * Consume the next byte + * + * @access private + * @return mixed The next byte, or false, if there is no more data + */ + public function consume() + { + if (isset($this->data[$this->position])) + { + $this->consumed .= $this->data[$this->position]; + return $this->data[$this->position++]; + } + else + { + return false; + } + } - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } + /** + * Consume a range of characters + * + * @access private + * @param string $chars Characters to consume + * @return mixed A series of characters that match the range, or false + */ + public function consume_range($chars) + { + if ($len = strspn($this->data, $chars, $this->position)) + { + $data = substr($this->data, $this->position, $len); + $this->consumed .= $data; + $this->position += $len; + return $data; + } + else + { + return false; + } + } - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } + /** + * Unconsume one byte + * + * @access private + */ + public function unconsume() + { + $this->consumed = substr($this->consumed, 0, -1); + $this->position--; + } - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } + /** + * Decode an entity + * + * @access private + */ + public function entity() + { + switch ($this->consume()) + { + case "\x09": + case "\x0A": + case "\x0B": + case "\x0B": + case "\x0C": + case "\x20": + case "\x3C": + case "\x26": + case false: + break; - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } + case "\x23": + switch ($this->consume()) + { + case "\x78": + case "\x58": + $range = '0123456789ABCDEFabcdef'; + $hex = true; + break; - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } + default: + $range = '0123456789'; + $hex = false; + $this->unconsume(); + break; + } - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } + if ($codepoint = $this->consume_range($range)) + { + static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } + if ($hex) + { + $codepoint = hexdec($codepoint); + } + else + { + $codepoint = intval($codepoint); } - } - } - // If we have standalone media:content tags, loop through them. - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) - { - foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + if (isset($windows_1252_specials[$codepoint])) { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + $replacement = $windows_1252_specials[$codepoint]; + } + else + { + $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); + } - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; + if (!in_array($this->consume(), array(';', false), true)) + { + $this->unconsume(); + } - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['url'])) - { - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + $consumed_length = strlen($this->consumed); + $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); + $this->position += strlen($replacement) - $consumed_length; + } + break; - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } + default: + static $entities = array( + 'Aacute' => "\xC3\x81", + 'aacute' => "\xC3\xA1", + 'Aacute;' => "\xC3\x81", + 'aacute;' => "\xC3\xA1", + 'Acirc' => "\xC3\x82", + 'acirc' => "\xC3\xA2", + 'Acirc;' => "\xC3\x82", + 'acirc;' => "\xC3\xA2", + 'acute' => "\xC2\xB4", + 'acute;' => "\xC2\xB4", + 'AElig' => "\xC3\x86", + 'aelig' => "\xC3\xA6", + 'AElig;' => "\xC3\x86", + 'aelig;' => "\xC3\xA6", + 'Agrave' => "\xC3\x80", + 'agrave' => "\xC3\xA0", + 'Agrave;' => "\xC3\x80", + 'agrave;' => "\xC3\xA0", + 'alefsym;' => "\xE2\x84\xB5", + 'Alpha;' => "\xCE\x91", + 'alpha;' => "\xCE\xB1", + 'AMP' => "\x26", + 'amp' => "\x26", + 'AMP;' => "\x26", + 'amp;' => "\x26", + 'and;' => "\xE2\x88\xA7", + 'ang;' => "\xE2\x88\xA0", + 'apos;' => "\x27", + 'Aring' => "\xC3\x85", + 'aring' => "\xC3\xA5", + 'Aring;' => "\xC3\x85", + 'aring;' => "\xC3\xA5", + 'asymp;' => "\xE2\x89\x88", + 'Atilde' => "\xC3\x83", + 'atilde' => "\xC3\xA3", + 'Atilde;' => "\xC3\x83", + 'atilde;' => "\xC3\xA3", + 'Auml' => "\xC3\x84", + 'auml' => "\xC3\xA4", + 'Auml;' => "\xC3\x84", + 'auml;' => "\xC3\xA4", + 'bdquo;' => "\xE2\x80\x9E", + 'Beta;' => "\xCE\x92", + 'beta;' => "\xCE\xB2", + 'brvbar' => "\xC2\xA6", + 'brvbar;' => "\xC2\xA6", + 'bull;' => "\xE2\x80\xA2", + 'cap;' => "\xE2\x88\xA9", + 'Ccedil' => "\xC3\x87", + 'ccedil' => "\xC3\xA7", + 'Ccedil;' => "\xC3\x87", + 'ccedil;' => "\xC3\xA7", + 'cedil' => "\xC2\xB8", + 'cedil;' => "\xC2\xB8", + 'cent' => "\xC2\xA2", + 'cent;' => "\xC2\xA2", + 'Chi;' => "\xCE\xA7", + 'chi;' => "\xCF\x87", + 'circ;' => "\xCB\x86", + 'clubs;' => "\xE2\x99\xA3", + 'cong;' => "\xE2\x89\x85", + 'COPY' => "\xC2\xA9", + 'copy' => "\xC2\xA9", + 'COPY;' => "\xC2\xA9", + 'copy;' => "\xC2\xA9", + 'crarr;' => "\xE2\x86\xB5", + 'cup;' => "\xE2\x88\xAA", + 'curren' => "\xC2\xA4", + 'curren;' => "\xC2\xA4", + 'Dagger;' => "\xE2\x80\xA1", + 'dagger;' => "\xE2\x80\xA0", + 'dArr;' => "\xE2\x87\x93", + 'darr;' => "\xE2\x86\x93", + 'deg' => "\xC2\xB0", + 'deg;' => "\xC2\xB0", + 'Delta;' => "\xCE\x94", + 'delta;' => "\xCE\xB4", + 'diams;' => "\xE2\x99\xA6", + 'divide' => "\xC3\xB7", + 'divide;' => "\xC3\xB7", + 'Eacute' => "\xC3\x89", + 'eacute' => "\xC3\xA9", + 'Eacute;' => "\xC3\x89", + 'eacute;' => "\xC3\xA9", + 'Ecirc' => "\xC3\x8A", + 'ecirc' => "\xC3\xAA", + 'Ecirc;' => "\xC3\x8A", + 'ecirc;' => "\xC3\xAA", + 'Egrave' => "\xC3\x88", + 'egrave' => "\xC3\xA8", + 'Egrave;' => "\xC3\x88", + 'egrave;' => "\xC3\xA8", + 'empty;' => "\xE2\x88\x85", + 'emsp;' => "\xE2\x80\x83", + 'ensp;' => "\xE2\x80\x82", + 'Epsilon;' => "\xCE\x95", + 'epsilon;' => "\xCE\xB5", + 'equiv;' => "\xE2\x89\xA1", + 'Eta;' => "\xCE\x97", + 'eta;' => "\xCE\xB7", + 'ETH' => "\xC3\x90", + 'eth' => "\xC3\xB0", + 'ETH;' => "\xC3\x90", + 'eth;' => "\xC3\xB0", + 'Euml' => "\xC3\x8B", + 'euml' => "\xC3\xAB", + 'Euml;' => "\xC3\x8B", + 'euml;' => "\xC3\xAB", + 'euro;' => "\xE2\x82\xAC", + 'exist;' => "\xE2\x88\x83", + 'fnof;' => "\xC6\x92", + 'forall;' => "\xE2\x88\x80", + 'frac12' => "\xC2\xBD", + 'frac12;' => "\xC2\xBD", + 'frac14' => "\xC2\xBC", + 'frac14;' => "\xC2\xBC", + 'frac34' => "\xC2\xBE", + 'frac34;' => "\xC2\xBE", + 'frasl;' => "\xE2\x81\x84", + 'Gamma;' => "\xCE\x93", + 'gamma;' => "\xCE\xB3", + 'ge;' => "\xE2\x89\xA5", + 'GT' => "\x3E", + 'gt' => "\x3E", + 'GT;' => "\x3E", + 'gt;' => "\x3E", + 'hArr;' => "\xE2\x87\x94", + 'harr;' => "\xE2\x86\x94", + 'hearts;' => "\xE2\x99\xA5", + 'hellip;' => "\xE2\x80\xA6", + 'Iacute' => "\xC3\x8D", + 'iacute' => "\xC3\xAD", + 'Iacute;' => "\xC3\x8D", + 'iacute;' => "\xC3\xAD", + 'Icirc' => "\xC3\x8E", + 'icirc' => "\xC3\xAE", + 'Icirc;' => "\xC3\x8E", + 'icirc;' => "\xC3\xAE", + 'iexcl' => "\xC2\xA1", + 'iexcl;' => "\xC2\xA1", + 'Igrave' => "\xC3\x8C", + 'igrave' => "\xC3\xAC", + 'Igrave;' => "\xC3\x8C", + 'igrave;' => "\xC3\xAC", + 'image;' => "\xE2\x84\x91", + 'infin;' => "\xE2\x88\x9E", + 'int;' => "\xE2\x88\xAB", + 'Iota;' => "\xCE\x99", + 'iota;' => "\xCE\xB9", + 'iquest' => "\xC2\xBF", + 'iquest;' => "\xC2\xBF", + 'isin;' => "\xE2\x88\x88", + 'Iuml' => "\xC3\x8F", + 'iuml' => "\xC3\xAF", + 'Iuml;' => "\xC3\x8F", + 'iuml;' => "\xC3\xAF", + 'Kappa;' => "\xCE\x9A", + 'kappa;' => "\xCE\xBA", + 'Lambda;' => "\xCE\x9B", + 'lambda;' => "\xCE\xBB", + 'lang;' => "\xE3\x80\x88", + 'laquo' => "\xC2\xAB", + 'laquo;' => "\xC2\xAB", + 'lArr;' => "\xE2\x87\x90", + 'larr;' => "\xE2\x86\x90", + 'lceil;' => "\xE2\x8C\x88", + 'ldquo;' => "\xE2\x80\x9C", + 'le;' => "\xE2\x89\xA4", + 'lfloor;' => "\xE2\x8C\x8A", + 'lowast;' => "\xE2\x88\x97", + 'loz;' => "\xE2\x97\x8A", + 'lrm;' => "\xE2\x80\x8E", + 'lsaquo;' => "\xE2\x80\xB9", + 'lsquo;' => "\xE2\x80\x98", + 'LT' => "\x3C", + 'lt' => "\x3C", + 'LT;' => "\x3C", + 'lt;' => "\x3C", + 'macr' => "\xC2\xAF", + 'macr;' => "\xC2\xAF", + 'mdash;' => "\xE2\x80\x94", + 'micro' => "\xC2\xB5", + 'micro;' => "\xC2\xB5", + 'middot' => "\xC2\xB7", + 'middot;' => "\xC2\xB7", + 'minus;' => "\xE2\x88\x92", + 'Mu;' => "\xCE\x9C", + 'mu;' => "\xCE\xBC", + 'nabla;' => "\xE2\x88\x87", + 'nbsp' => "\xC2\xA0", + 'nbsp;' => "\xC2\xA0", + 'ndash;' => "\xE2\x80\x93", + 'ne;' => "\xE2\x89\xA0", + 'ni;' => "\xE2\x88\x8B", + 'not' => "\xC2\xAC", + 'not;' => "\xC2\xAC", + 'notin;' => "\xE2\x88\x89", + 'nsub;' => "\xE2\x8A\x84", + 'Ntilde' => "\xC3\x91", + 'ntilde' => "\xC3\xB1", + 'Ntilde;' => "\xC3\x91", + 'ntilde;' => "\xC3\xB1", + 'Nu;' => "\xCE\x9D", + 'nu;' => "\xCE\xBD", + 'Oacute' => "\xC3\x93", + 'oacute' => "\xC3\xB3", + 'Oacute;' => "\xC3\x93", + 'oacute;' => "\xC3\xB3", + 'Ocirc' => "\xC3\x94", + 'ocirc' => "\xC3\xB4", + 'Ocirc;' => "\xC3\x94", + 'ocirc;' => "\xC3\xB4", + 'OElig;' => "\xC5\x92", + 'oelig;' => "\xC5\x93", + 'Ograve' => "\xC3\x92", + 'ograve' => "\xC3\xB2", + 'Ograve;' => "\xC3\x92", + 'ograve;' => "\xC3\xB2", + 'oline;' => "\xE2\x80\xBE", + 'Omega;' => "\xCE\xA9", + 'omega;' => "\xCF\x89", + 'Omicron;' => "\xCE\x9F", + 'omicron;' => "\xCE\xBF", + 'oplus;' => "\xE2\x8A\x95", + 'or;' => "\xE2\x88\xA8", + 'ordf' => "\xC2\xAA", + 'ordf;' => "\xC2\xAA", + 'ordm' => "\xC2\xBA", + 'ordm;' => "\xC2\xBA", + 'Oslash' => "\xC3\x98", + 'oslash' => "\xC3\xB8", + 'Oslash;' => "\xC3\x98", + 'oslash;' => "\xC3\xB8", + 'Otilde' => "\xC3\x95", + 'otilde' => "\xC3\xB5", + 'Otilde;' => "\xC3\x95", + 'otilde;' => "\xC3\xB5", + 'otimes;' => "\xE2\x8A\x97", + 'Ouml' => "\xC3\x96", + 'ouml' => "\xC3\xB6", + 'Ouml;' => "\xC3\x96", + 'ouml;' => "\xC3\xB6", + 'para' => "\xC2\xB6", + 'para;' => "\xC2\xB6", + 'part;' => "\xE2\x88\x82", + 'permil;' => "\xE2\x80\xB0", + 'perp;' => "\xE2\x8A\xA5", + 'Phi;' => "\xCE\xA6", + 'phi;' => "\xCF\x86", + 'Pi;' => "\xCE\xA0", + 'pi;' => "\xCF\x80", + 'piv;' => "\xCF\x96", + 'plusmn' => "\xC2\xB1", + 'plusmn;' => "\xC2\xB1", + 'pound' => "\xC2\xA3", + 'pound;' => "\xC2\xA3", + 'Prime;' => "\xE2\x80\xB3", + 'prime;' => "\xE2\x80\xB2", + 'prod;' => "\xE2\x88\x8F", + 'prop;' => "\xE2\x88\x9D", + 'Psi;' => "\xCE\xA8", + 'psi;' => "\xCF\x88", + 'QUOT' => "\x22", + 'quot' => "\x22", + 'QUOT;' => "\x22", + 'quot;' => "\x22", + 'radic;' => "\xE2\x88\x9A", + 'rang;' => "\xE3\x80\x89", + 'raquo' => "\xC2\xBB", + 'raquo;' => "\xC2\xBB", + 'rArr;' => "\xE2\x87\x92", + 'rarr;' => "\xE2\x86\x92", + 'rceil;' => "\xE2\x8C\x89", + 'rdquo;' => "\xE2\x80\x9D", + 'real;' => "\xE2\x84\x9C", + 'REG' => "\xC2\xAE", + 'reg' => "\xC2\xAE", + 'REG;' => "\xC2\xAE", + 'reg;' => "\xC2\xAE", + 'rfloor;' => "\xE2\x8C\x8B", + 'Rho;' => "\xCE\xA1", + 'rho;' => "\xCF\x81", + 'rlm;' => "\xE2\x80\x8F", + 'rsaquo;' => "\xE2\x80\xBA", + 'rsquo;' => "\xE2\x80\x99", + 'sbquo;' => "\xE2\x80\x9A", + 'Scaron;' => "\xC5\xA0", + 'scaron;' => "\xC5\xA1", + 'sdot;' => "\xE2\x8B\x85", + 'sect' => "\xC2\xA7", + 'sect;' => "\xC2\xA7", + 'shy' => "\xC2\xAD", + 'shy;' => "\xC2\xAD", + 'Sigma;' => "\xCE\xA3", + 'sigma;' => "\xCF\x83", + 'sigmaf;' => "\xCF\x82", + 'sim;' => "\xE2\x88\xBC", + 'spades;' => "\xE2\x99\xA0", + 'sub;' => "\xE2\x8A\x82", + 'sube;' => "\xE2\x8A\x86", + 'sum;' => "\xE2\x88\x91", + 'sup;' => "\xE2\x8A\x83", + 'sup1' => "\xC2\xB9", + 'sup1;' => "\xC2\xB9", + 'sup2' => "\xC2\xB2", + 'sup2;' => "\xC2\xB2", + 'sup3' => "\xC2\xB3", + 'sup3;' => "\xC2\xB3", + 'supe;' => "\xE2\x8A\x87", + 'szlig' => "\xC3\x9F", + 'szlig;' => "\xC3\x9F", + 'Tau;' => "\xCE\xA4", + 'tau;' => "\xCF\x84", + 'there4;' => "\xE2\x88\xB4", + 'Theta;' => "\xCE\x98", + 'theta;' => "\xCE\xB8", + 'thetasym;' => "\xCF\x91", + 'thinsp;' => "\xE2\x80\x89", + 'THORN' => "\xC3\x9E", + 'thorn' => "\xC3\xBE", + 'THORN;' => "\xC3\x9E", + 'thorn;' => "\xC3\xBE", + 'tilde;' => "\xCB\x9C", + 'times' => "\xC3\x97", + 'times;' => "\xC3\x97", + 'TRADE;' => "\xE2\x84\xA2", + 'trade;' => "\xE2\x84\xA2", + 'Uacute' => "\xC3\x9A", + 'uacute' => "\xC3\xBA", + 'Uacute;' => "\xC3\x9A", + 'uacute;' => "\xC3\xBA", + 'uArr;' => "\xE2\x87\x91", + 'uarr;' => "\xE2\x86\x91", + 'Ucirc' => "\xC3\x9B", + 'ucirc' => "\xC3\xBB", + 'Ucirc;' => "\xC3\x9B", + 'ucirc;' => "\xC3\xBB", + 'Ugrave' => "\xC3\x99", + 'ugrave' => "\xC3\xB9", + 'Ugrave;' => "\xC3\x99", + 'ugrave;' => "\xC3\xB9", + 'uml' => "\xC2\xA8", + 'uml;' => "\xC2\xA8", + 'upsih;' => "\xCF\x92", + 'Upsilon;' => "\xCE\xA5", + 'upsilon;' => "\xCF\x85", + 'Uuml' => "\xC3\x9C", + 'uuml' => "\xC3\xBC", + 'Uuml;' => "\xC3\x9C", + 'uuml;' => "\xC3\xBC", + 'weierp;' => "\xE2\x84\x98", + 'Xi;' => "\xCE\x9E", + 'xi;' => "\xCE\xBE", + 'Yacute' => "\xC3\x9D", + 'yacute' => "\xC3\xBD", + 'Yacute;' => "\xC3\x9D", + 'yacute;' => "\xC3\xBD", + 'yen' => "\xC2\xA5", + 'yen;' => "\xC2\xA5", + 'yuml' => "\xC3\xBF", + 'Yuml;' => "\xC5\xB8", + 'yuml;' => "\xC3\xBF", + 'Zeta;' => "\xCE\x96", + 'zeta;' => "\xCE\xB6", + 'zwj;' => "\xE2\x80\x8D", + 'zwnj;' => "\xE2\x80\x8C" + ); - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - else - { - $categories = null; - } + for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) + { + $consumed = substr($this->consumed, 1); + if (isset($entities[$consumed])) + { + $match = $consumed; + } + } - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } + if ($match !== null) + { + $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); + $this->position += strlen($entities[$match]) - strlen($consumed) - 1; + } + break; + } + } +} - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } +/** + * Handles everything related to enclosures (including Media RSS and iTunes RSS) + * + * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()} + * + * This class can be overloaded with {@see SimplePie::set_enclosure_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Enclosure +{ + /** + * @var string + * @see get_bitrate() + */ + var $bitrate; - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } - - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } + /** + * @var array + * @see get_captions() + */ + var $captions; - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } + /** + * @var array + * @see get_categories() + */ + var $categories; - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } + /** + * @var int + * @see get_channels() + */ + var $channels; - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } + /** + * @var SimplePie_Copyright + * @see get_copyright() + */ + var $copyright; - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } + /** + * @var array + * @see get_credits() + */ + var $credits; - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } + /** + * @var string + * @see get_description() + */ + var $description; - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } + /** + * @var int + * @see get_duration() + */ + var $duration; - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } - } - } + /** + * @var string + * @see get_expression() + */ + var $expression; - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + /** + * @var string + * @see get_framerate() + */ + var $framerate; - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } + /** + * @var string + * @see get_handler() + */ + var $handler; - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } + /** + * @var array + * @see get_hashes() + */ + var $hashes; - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + /** + * @var string + * @see get_height() + */ + var $height; - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } + /** + * @deprecated + * @var null + */ + var $javascript; - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } + /** + * @var array + * @see get_keywords() + */ + var $keywords; - if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure')) - { - if (isset($enclosure[0]['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; + /** + * @var string + * @see get_language() + */ + var $lang; - $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); - if (isset($enclosure[0]['attribs']['']['type'])) - { - $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($enclosure[0]['attribs']['']['length'])) - { - $length = ceil($enclosure[0]['attribs']['']['length']); - } + /** + * @var string + * @see get_length() + */ + var $length; - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } + /** + * @var string + * @see get_link() + */ + var $link; - if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) - { - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } + /** + * @var string + * @see get_medium() + */ + var $medium; + + /** + * @var string + * @see get_player() + */ + var $player; + + /** + * @var array + * @see get_ratings() + */ + var $ratings; + + /** + * @var array + * @see get_restrictions() + */ + var $restrictions; + + /** + * @var string + * @see get_sampling_rate() + */ + var $samplingrate; + + /** + * @var array + * @see get_thumbnails() + */ + var $thumbnails; + + /** + * @var string + * @see get_title() + */ + var $title; + + /** + * @var string + * @see get_type() + */ + var $type; + + /** + * @var string + * @see get_width() + */ + var $width; - $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures'])); + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + * + * @uses idna_convert If available, this will convert an IDN + */ + public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) + { + $this->bitrate = $bitrate; + $this->captions = $captions; + $this->categories = $categories; + $this->channels = $channels; + $this->copyright = $copyright; + $this->credits = $credits; + $this->description = $description; + $this->duration = $duration; + $this->expression = $expression; + $this->framerate = $framerate; + $this->hashes = $hashes; + $this->height = $height; + $this->keywords = $keywords; + $this->lang = $lang; + $this->length = $length; + $this->link = $link; + $this->medium = $medium; + $this->player = $player; + $this->ratings = $ratings; + $this->restrictions = $restrictions; + $this->samplingrate = $samplingrate; + $this->thumbnails = $thumbnails; + $this->title = $title; + $this->type = $type; + $this->width = $width; + + if (class_exists('idna_convert')) + { + $idn = new idna_convert(); + $parsed = SimplePie_Misc::parse_url($link); + $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } - if (!empty($this->data['enclosures'])) + $this->handler = $this->get_handler(); // Needs to load last + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the bitrate + * + * @return string|null + */ + public function get_bitrate() + { + if ($this->bitrate !== null) { - return $this->data['enclosures']; + return $this->bitrate; } else { @@ -5585,15 +5686,18 @@ class SimplePie_Item } } - function get_latitude() + /** + * Get a single caption + * + * @param int $key + * @return SimplePie_Caption|null + */ + public function get_caption($key = 0) { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + $captions = $this->get_captions(); + if (isset($captions[$key])) { - return (float) $match[1]; + return $captions[$key]; } else { @@ -5601,19 +5705,35 @@ class SimplePie_Item } } - function get_longitude() + /** + * Get all captions + * + * @return array|null Array of {@see SimplePie_Caption} objects + */ + public function get_captions() { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + if ($this->captions !== null) { - return (float) $return[0]['data']; + return $this->captions; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + else { - return (float) $return[0]['data']; + return null; } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + } + + /** + * Get a single category + * + * @param int $key + * @return SimplePie_Category|null + */ + public function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) { - return (float) $match[2]; + return $categories[$key]; } else { @@ -5621,11 +5741,16 @@ class SimplePie_Item } } - function get_source() + /** + * Get all categories + * + * @return array|null Array of {@see SimplePie_Category} objects + */ + public function get_categories() { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) + if ($this->categories !== null) { - return new $this->feed->source_class($this, $return[0]); + return $this->categories; } else { @@ -5634,28 +5759,15 @@ class SimplePie_Item } /** - * Creates the add_to_* methods' return data + * Get the number of audio channels * - * @access private - * @param string $item_url String to prefix to the item permalink - * @param string $title_url String to prefix to the item title - * (and suffix to the item permalink) - * @return mixed URL if feed exists, false otherwise + * @return int|null */ - function add_to_service($item_url, $title_url = null, $summary_url = null) + public function get_channels() { - if ($this->get_permalink() !== null) + if ($this->channels !== null) { - $return = $item_url . rawurlencode($this->get_permalink()); - if ($title_url !== null && $this->get_title() !== null) - { - $return .= $title_url . rawurlencode($this->get_title()); - } - if ($summary_url !== null && $this->get_description() !== null) - { - $return .= $summary_url . rawurlencode($this->get_description()); - } - return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI); + return $this->channels; } else { @@ -5663,98 +5775,194 @@ class SimplePie_Item } } - function add_to_blinklist() - { - return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title='); - } - - function add_to_blogmarks() - { - return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title='); - } - - function add_to_delicious() - { - return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title='); - } - - function add_to_digg() + /** + * Get the copyright information + * + * @return SimplePie_Copyright|null + */ + public function get_copyright() { - return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext='); + if ($this->copyright !== null) + { + return $this->copyright; + } + else + { + return null; + } } - function add_to_furl() + /** + * Get a single credit + * + * @param int $key + * @return SimplePie_Credit|null + */ + public function get_credit($key = 0) { - return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t='); + $credits = $this->get_credits(); + if (isset($credits[$key])) + { + return $credits[$key]; + } + else + { + return null; + } } - function add_to_magnolia() + /** + * Get all credits + * + * @return array|null Array of {@see SimplePie_Credit} objects + */ + public function get_credits() { - return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title='); + if ($this->credits !== null) + { + return $this->credits; + } + else + { + return null; + } } - function add_to_myweb20() + /** + * Get the description of the enclosure + * + * @return string|null + */ + public function get_description() { - return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t='); + if ($this->description !== null) + { + return $this->description; + } + else + { + return null; + } } - function add_to_newsvine() + /** + * Get the duration of the enclosure + * + * @param string $convert Convert seconds into hh:mm:ss + * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found) + */ + public function get_duration($convert = false) { - return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h='); - } - - function add_to_reddit() - { - return $this->add_to_service('http://reddit.com/submit?url=', '&title='); - } - - function add_to_segnalo() - { - return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title='); - } - - function add_to_simpy() - { - return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title='); + if ($this->duration !== null) + { + if ($convert) + { + $time = SimplePie_Misc::time_hms($this->duration); + return $time; + } + else + { + return $this->duration; + } + } + else + { + return null; + } } - function add_to_spurl() + /** + * Get the expression + * + * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full' + */ + public function get_expression() { - return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title='); + if ($this->expression !== null) + { + return $this->expression; + } + else + { + return 'full'; + } } - function add_to_wists() + /** + * Get the file extension + * + * @return string|null + */ + public function get_extension() { - return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title='); + if ($this->link !== null) + { + $url = SimplePie_Misc::parse_url($this->link); + if ($url['path'] !== '') + { + return pathinfo($url['path'], PATHINFO_EXTENSION); + } + } + return null; } - function search_technorati() + /** + * Get the framerate (in frames-per-second) + * + * @return string|null + */ + public function get_framerate() { - return $this->add_to_service('http://www.technorati.com/search/'); + if ($this->framerate !== null) + { + return $this->framerate; + } + else + { + return null; + } } -} - -class SimplePie_Source -{ - var $item; - var $data = array(); - function SimplePie_Source($item, $data) + /** + * Get the preferred handler + * + * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3' + */ + public function get_handler() { - $this->item = $item; - $this->data = $data; + return $this->get_real_type(true); } - function __toString() + /** + * Get a single hash + * + * @link http://www.rssboard.org/media-rss#media-hash + * @param int $key + * @return string|null Hash as per `media:hash`, prefixed with "$algo:" + */ + public function get_hash($key = 0) { - return md5(serialize($this->data)); + $hashes = $this->get_hashes(); + if (isset($hashes[$key])) + { + return $hashes[$key]; + } + else + { + return null; + } } - function get_source_tags($namespace, $tag) + /** + * Get all credits + * + * @return array|null Array of strings, see {@see get_hash()} + */ + public function get_hashes() { - if (isset($this->data['child'][$namespace][$tag])) + if ($this->hashes !== null) { - return $this->data['child'][$namespace][$tag]; + return $this->hashes; } else { @@ -5762,50 +5970,122 @@ class SimplePie_Source } } - function get_base($element = array()) + /** + * Get the height + * + * @return string|null + */ + public function get_height() { - return $this->item->get_base($element); + if ($this->height !== null) + { + return $this->height; + } + else + { + return null; + } } - function sanitize($data, $type, $base = '') + /** + * Get the language + * + * @link http://tools.ietf.org/html/rfc3066 + * @return string|null Language code as per RFC 3066 + */ + public function get_language() { - return $this->item->sanitize($data, $type, $base); + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } } - function get_item() + /** + * Get a single keyword + * + * @param int $key + * @return string|null + */ + public function get_keyword($key = 0) { - return $this->item; + $keywords = $this->get_keywords(); + if (isset($keywords[$key])) + { + return $keywords[$key]; + } + else + { + return null; + } } - function get_title() + /** + * Get all keywords + * + * @return array|null Array of strings + */ + public function get_keywords() { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + if ($this->keywords !== null) { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return $this->keywords; } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + else { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + return null; } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + } + + /** + * Get length + * + * @return float Length in bytes + */ + public function get_length() + { + if ($this->length !== null) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + return $this->length; } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + else { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + return null; } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + } + + /** + * Get the URL + * + * @return string|null + */ + public function get_link() + { + if ($this->link !== null) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + return urldecode($this->link); } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + else { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return null; } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + } + + /** + * Get the medium + * + * @link http://www.rssboard.org/media-rss#media-content + * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable' + */ + public function get_medium() + { + if ($this->medium !== null) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return $this->medium; } else { @@ -5813,12 +6093,17 @@ class SimplePie_Source } } - function get_category($key = 0) + /** + * Get the player URL + * + * Typically the same as {@see get_permalink()} + * @return string|null Player URL + */ + public function get_player() { - $categories = $this->get_categories(); - if (isset($categories[$key])) + if ($this->player !== null) { - return $categories[$key]; + return $this->player; } else { @@ -5826,56 +6111,54 @@ class SimplePie_Source } } - function get_categories() + /** + * Get a single rating + * + * @param int $key + * @return SimplePie_Rating|null + */ + public function get_rating($key = 0) { - $categories = array(); - - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + $ratings = $this->get_ratings(); + if (isset($ratings[$key])) { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->item->feed->category_class($term, $scheme, $label); + return $ratings[$key]; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + else { - // This is really the label, but keep this as the term also for BC. - // Label will also work on retrieving because that falls back to term. - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - if (isset($category['attribs']['']['domain'])) - { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = null; - } - $categories[] = new $this->item->feed->category_class($term, $scheme, null); + return null; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + } + + /** + * Get all ratings + * + * @return array|null Array of {@see SimplePie_Rating} objects + */ + public function get_ratings() + { + if ($this->ratings !== null) { - $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return $this->ratings; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + else { - $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return null; } + } - if (!empty($categories)) + /** + * Get a single restriction + * + * @param int $key + * @return SimplePie_Restriction|null + */ + public function get_restriction($key = 0) + { + $restrictions = $this->get_restrictions(); + if (isset($restrictions[$key])) { - return SimplePie_Misc::array_unique($categories); + return $restrictions[$key]; } else { @@ -5883,12 +6166,16 @@ class SimplePie_Source } } - function get_author($key = 0) + /** + * Get all restrictions + * + * @return array|null Array of {@see SimplePie_Restriction} objects + */ + public function get_restrictions() { - $authors = $this->get_authors(); - if (isset($authors[$key])) + if ($this->restrictions !== null) { - return $authors[$key]; + return $this->restrictions; } else { @@ -5896,69 +6183,53 @@ class SimplePie_Source } } - function get_authors() + /** + * Get the sampling rate (in kHz) + * + * @return string|null + */ + public function get_sampling_rate() { - $authors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + if ($this->samplingrate !== null) { - $name = null; - $uri = null; - $email = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $authors[] = new $this->item->feed->author_class($name, $uri, $email); - } - } - if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] = new $this->item->feed->author_class($name, $url, $email); - } + return $this->samplingrate; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + else { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return null; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + } + + /** + * Get the file size (in MiB) + * + * @return float|null File size in mebibytes (1048 bytes) + */ + public function get_size() + { + $length = $this->get_length(); + if ($length !== null) { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return round($length/1048576, 2); } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + else { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + return null; } + } - if (!empty($authors)) + /** + * Get a single thumbnail + * + * @param int $key + * @return string|null Thumbnail URL + */ + public function get_thumbnail($key = 0) + { + $thumbnails = $this->get_thumbnails(); + if (isset($thumbnails[$key])) { - return SimplePie_Misc::array_unique($authors); + return $thumbnails[$key]; } else { @@ -5966,12 +6237,16 @@ class SimplePie_Source } } - function get_contributor($key = 0) + /** + * Get all thumbnails + * + * @return array|null Array of thumbnail URLs + */ + public function get_thumbnails() { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) + if ($this->thumbnails !== null) { - return $contributors[$key]; + return $this->thumbnails; } else { @@ -5979,57 +6254,34 @@ class SimplePie_Source } } - function get_contributors() + /** + * Get the title + * + * @return string|null + */ + public function get_title() { - $contributors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + if ($this->title !== null) { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] = new $this->item->feed->author_class($name, $uri, $email); - } + return $this->title; } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + else { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] = new $this->item->feed->author_class($name, $url, $email); - } + return null; } + } - if (!empty($contributors)) + /** + * Get mimetype of the enclosure + * + * @see get_real_type() + * @return string|null MIME type + */ + public function get_type() + { + if ($this->type !== null) { - return SimplePie_Misc::array_unique($contributors); + return $this->type; } else { @@ -6037,12 +6289,16 @@ class SimplePie_Source } } - function get_link($key = 0, $rel = 'alternate') + /** + * Get the width + * + * @return string|null + */ + public function get_width() { - $links = $this->get_links($rel); - if (isset($links[$key])) + if ($this->width !== null) { - return $links[$key]; + return $this->width; } else { @@ -6051,1517 +6307,2688 @@ class SimplePie_Source } /** - * Added for parity between the parent-level and the item/entry-level. + * Embed the enclosure using `<embed>` + * + * @deprecated Use the second parameter to {@see embed} instead + * + * @param array|string $options See first paramter to {@see embed} + * @return string HTML string to output */ - function get_permalink() + public function native_embed($options='') { - return $this->get_link(0); + return $this->embed($options, true); } - function get_links($rel = 'alternate') + /** + * Embed the enclosure using Javascript + * + * `$options` is an array or comma-separated key:value string, with the + * following properties: + * + * - `alt` (string): Alternate content for when an end-user does not have + * the appropriate handler installed or when a file type is + * unsupported. Can be any text or HTML. Defaults to blank. + * - `altclass` (string): If a file type is unsupported, the end-user will + * see the alt text (above) linked directly to the content. That link + * will have this value as its class name. Defaults to blank. + * - `audio` (string): This is an image that should be used as a + * placeholder for audio files before they're loaded (QuickTime-only). + * Can be any relative or absolute URL. Defaults to blank. + * - `bgcolor` (string): The background color for the media, if not + * already transparent. Defaults to `#ffffff`. + * - `height` (integer): The height of the embedded media. Accepts any + * numeric pixel value (such as `360`) or `auto`. Defaults to `auto`, + * and it is recommended that you use this default. + * - `loop` (boolean): Do you want the media to loop when its done? + * Defaults to `false`. + * - `mediaplayer` (string): The location of the included + * `mediaplayer.swf` file. This allows for the playback of Flash Video + * (`.flv`) files, and is the default handler for non-Odeo MP3's. + * Defaults to blank. + * - `video` (string): This is an image that should be used as a + * placeholder for video files before they're loaded (QuickTime-only). + * Can be any relative or absolute URL. Defaults to blank. + * - `width` (integer): The width of the embedded media. Accepts any + * numeric pixel value (such as `480`) or `auto`. Defaults to `auto`, + * and it is recommended that you use this default. + * - `widescreen` (boolean): Is the enclosure widescreen or standard? + * This applies only to video enclosures, and will automatically resize + * the content appropriately. Defaults to `false`, implying 4:3 mode. + * + * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto` + * will default to 480x360 video resolution. Widescreen (16:9) mode with + * `width` and `height` set to `auto` will default to 480x270 video resolution. + * + * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. + * @param array|string $options Comma-separated key:value list, or array + * @param bool $native Use `<embed>` + * @return string HTML string to output + */ + public function embed($options = '', $native = false) { - if (!isset($this->data['links'])) + // Set up defaults + $audio = ''; + $video = ''; + $alt = ''; + $altclass = ''; + $loop = 'false'; + $width = 'auto'; + $height = 'auto'; + $bgcolor = '#ffffff'; + $mediaplayer = ''; + $widescreen = false; + $handler = $this->get_handler(); + $type = $this->get_real_type(); + + // Process options and reassign values as necessary + if (is_array($options)) { - $this->data['links'] = array(); - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + extract($options); + } + else + { + $options = explode(',', $options); + foreach($options as $option) { - foreach ($links as $link) + $opt = explode(':', $option, 2); + if (isset($opt[0], $opt[1])) { - if (isset($link['attribs']['']['href'])) + $opt[0] = trim($opt[0]); + $opt[1] = trim($opt[1]); + switch ($opt[0]) { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + case 'audio': + $audio = $opt[1]; + break; + + case 'video': + $video = $opt[1]; + break; + + case 'alt': + $alt = $opt[1]; + break; + + case 'altclass': + $altclass = $opt[1]; + break; + + case 'loop': + $loop = $opt[1]; + break; + + case 'width': + $width = $opt[1]; + break; + + case 'height': + $height = $opt[1]; + break; + + case 'bgcolor': + $bgcolor = $opt[1]; + break; + + case 'mediaplayer': + $mediaplayer = $opt[1]; + break; + + case 'widescreen': + $widescreen = $opt[1]; + break; } } } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + } + + $mime = explode('/', $type, 2); + $mime = $mime[0]; + + // Process values for 'auto' + if ($width === 'auto') + { + if ($mime === 'video') { - foreach ($links as $link) + if ($height === 'auto') { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } + $width = 480; + } + elseif ($widescreen) + { + $width = round((intval($height)/9)*16); + } + else + { + $width = round((intval($height)/3)*4); } } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + else { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + $width = '100%'; } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + } + + if ($height === 'auto') + { + if ($mime === 'audio') { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + $height = 0; } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) + elseif ($mime === 'video') { - if (SimplePie_Misc::is_isegment_nz_nc($key)) + if ($width === 'auto') { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + if ($widescreen) { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + $height = 270; } else { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + $height = 360; } } - elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + elseif ($widescreen) { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + $height = round((intval($width)/16)*9); + } + else + { + $height = round((intval($width)/4)*3); } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + else + { + $height = 376; } } - - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else + elseif ($mime === 'audio') { - return null; + $height = 0; } - } - function get_description() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + // Set proper placeholder value + if ($mime === 'audio') { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + $placeholder = $audio; } - else + elseif ($mime === 'video') { - return null; + $placeholder = $video; } - } - function get_copyright() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } + $embed = ''; - function get_language() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['xml_lang'])) - { - return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else + // Flash + if ($handler === 'flash') { - return null; + if ($native) + { + $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>"; + } } - } - function get_latitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[1]; - } - else + // Flash Media Player file types. + // Preferred handler for MP3 file types. + elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) { - return null; + $height += 20; + if ($native) + { + $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>"; + } } - } - function get_longitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[2]; - } - else + // QuickTime 7 file types. Need to test with QuickTime 6. + // Only handle MP3's if the Flash Media Player is not present. + elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) { - return null; + $height += 16; + if ($native) + { + if ($placeholder !== '') + { + $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + } + else + { + $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + } + } + else + { + $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>"; + } } - } - function get_image_url() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) - { - return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else + // Windows Media + elseif ($handler === 'wmedia') { - return null; + $height += 45; + if ($native) + { + $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>"; + } } - } -} -class SimplePie_Author -{ - var $name; - var $link; - var $email; + // Everything else + else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>'; - // Constructor, used to input the data - function SimplePie_Author($name = null, $link = null, $email = null) - { - $this->name = $name; - $this->link = $link; - $this->email = $email; + return $embed; } - function __toString() + /** + * Get the real media type + * + * Often, feeds lie to us, necessitating a bit of deeper inspection. This + * converts types to their canonical representations based on the file + * extension + * + * @see get_type() + * @param bool $find_handler Internal use only, use {@see get_handler()} instead + * @return string MIME type + */ + public function get_real_type($find_handler = false) { - // There is no $this->data here - return md5(serialize($this)); - } + // Mime-types by handler. + $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash + $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player + $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime + $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media + $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 - function get_name() - { - if ($this->name !== null) + if ($this->get_type() !== null) { - return $this->name; + $type = strtolower($this->type); } else { - return null; + $type = null; } - } - function get_link() - { - if ($this->link !== null) - { - return $this->link; - } - else + // If we encounter an unsupported mime-type, check the file extension and guess intelligently. + if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) { - return null; - } - } + switch (strtolower($this->get_extension())) + { + // Audio mime-types + case 'aac': + case 'adts': + $type = 'audio/acc'; + break; - function get_email() - { - if ($this->email !== null) - { - return $this->email; - } - else - { - return null; - } - } -} + case 'aif': + case 'aifc': + case 'aiff': + case 'cdda': + $type = 'audio/aiff'; + break; -class SimplePie_Category -{ - var $term; - var $scheme; - var $label; + case 'bwf': + $type = 'audio/wav'; + break; - // Constructor, used to input the data - function SimplePie_Category($term = null, $scheme = null, $label = null) - { - $this->term = $term; - $this->scheme = $scheme; - $this->label = $label; - } + case 'kar': + case 'mid': + case 'midi': + case 'smf': + $type = 'audio/midi'; + break; - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } + case 'm4a': + $type = 'audio/x-m4a'; + break; - function get_term() - { - if ($this->term !== null) - { - return $this->term; - } - else - { - return null; - } - } + case 'mp3': + case 'swa': + $type = 'audio/mp3'; + break; - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; + case 'wav': + $type = 'audio/wav'; + break; + + case 'wax': + $type = 'audio/x-ms-wax'; + break; + + case 'wma': + $type = 'audio/x-ms-wma'; + break; + + // Video mime-types + case '3gp': + case '3gpp': + $type = 'video/3gpp'; + break; + + case '3g2': + case '3gp2': + $type = 'video/3gpp2'; + break; + + case 'asf': + $type = 'video/x-ms-asf'; + break; + + case 'flv': + $type = 'video/x-flv'; + break; + + case 'm1a': + case 'm1s': + case 'm1v': + case 'm15': + case 'm75': + case 'mp2': + case 'mpa': + case 'mpeg': + case 'mpg': + case 'mpm': + case 'mpv': + $type = 'video/mpeg'; + break; + + case 'm4v': + $type = 'video/x-m4v'; + break; + + case 'mov': + case 'qt': + $type = 'video/quicktime'; + break; + + case 'mp4': + case 'mpg4': + $type = 'video/mp4'; + break; + + case 'sdv': + $type = 'video/sd-video'; + break; + + case 'wm': + $type = 'video/x-ms-wm'; + break; + + case 'wmv': + $type = 'video/x-ms-wmv'; + break; + + case 'wvx': + $type = 'video/x-ms-wvx'; + break; + + // Flash mime-types + case 'spl': + $type = 'application/futuresplash'; + break; + + case 'swf': + $type = 'application/x-shockwave-flash'; + break; + } } - } - function get_label() - { - if ($this->label !== null) + if ($find_handler) { - return $this->label; + if (in_array($type, $types_flash)) + { + return 'flash'; + } + elseif (in_array($type, $types_fmedia)) + { + return 'fmedia'; + } + elseif (in_array($type, $types_quicktime)) + { + return 'quicktime'; + } + elseif (in_array($type, $types_wmedia)) + { + return 'wmedia'; + } + elseif (in_array($type, $types_mp3)) + { + return 'mp3'; + } + else + { + return null; + } } else { - return $this->get_term(); + return $type; } } } -class SimplePie_Enclosure +/** + * General SimplePie exception class + * + * @package SimplePie + */ +class SimplePie_Exception extends Exception { - var $bitrate; - var $captions; - var $categories; - var $channels; - var $copyright; - var $credits; - var $description; - var $duration; - var $expression; - var $framerate; - var $handler; - var $hashes; - var $height; - var $javascript; - var $keywords; - var $lang; - var $length; - var $link; - var $medium; - var $player; - var $ratings; - var $restrictions; - var $samplingrate; - var $thumbnails; - var $title; - var $type; - var $width; +} + +/** + * Used for fetching remote files and reading local files + * + * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support + * + * This class can be overloaded with {@see SimplePie::set_file_class()} + * + * @package SimplePie + * @subpackage HTTP + * @todo Move to properly supporting RFC2616 (HTTP/1.1) + */ +class SimplePie_File +{ + var $url; + var $useragent; + var $success = true; + var $headers = array(); + var $body; + var $status_code; + var $redirects = 0; + var $error; + var $method = SIMPLEPIE_FILE_SOURCE_NONE; - // Constructor, used to input the data - function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) + public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { - $this->bitrate = $bitrate; - $this->captions = $captions; - $this->categories = $categories; - $this->channels = $channels; - $this->copyright = $copyright; - $this->credits = $credits; - $this->description = $description; - $this->duration = $duration; - $this->expression = $expression; - $this->framerate = $framerate; - $this->hashes = $hashes; - $this->height = $height; - $this->javascript = $javascript; - $this->keywords = $keywords; - $this->lang = $lang; - $this->length = $length; - $this->link = $link; - $this->medium = $medium; - $this->player = $player; - $this->ratings = $ratings; - $this->restrictions = $restrictions; - $this->samplingrate = $samplingrate; - $this->thumbnails = $thumbnails; - $this->title = $title; - $this->type = $type; - $this->width = $width; if (class_exists('idna_convert')) { - $idn = new idna_convert; - $parsed = SimplePie_Misc::parse_url($link); - $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + $idn = new idna_convert(); + $parsed = SimplePie_Misc::parse_url($url); + $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } - $this->handler = $this->get_handler(); // Needs to load last - } + $this->url = $url; + $this->useragent = $useragent; + if (preg_match('/^http(s)?:\/\//i', $url)) + { + if ($useragent === null) + { + $useragent = ini_get('user_agent'); + $this->useragent = $useragent; + } + if (!is_array($headers)) + { + $headers = array(); + } + if (!$force_fsockopen && function_exists('curl_exec')) + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; + $fp = curl_init(); + $headers2 = array(); + foreach ($headers as $key => $value) + { + $headers2[] = "$key: $value"; + } + if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) + { + curl_setopt($fp, CURLOPT_ENCODING, ''); + } + curl_setopt($fp, CURLOPT_URL, $url); + curl_setopt($fp, CURLOPT_HEADER, 1); + curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_REFERER, $url); + curl_setopt($fp, CURLOPT_USERAGENT, $useragent); + curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); + if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) + { + curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); + } - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } + $this->headers = curl_exec($fp); + if (curl_errno($fp) === 23 || curl_errno($fp) === 61) + { + curl_setopt($fp, CURLOPT_ENCODING, 'none'); + $this->headers = curl_exec($fp); + } + if (curl_errno($fp)) + { + $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); + $this->success = false; + } + else + { + $info = curl_getinfo($fp); + curl_close($fp); + $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); + $this->headers = array_pop($this->headers); + $parser = new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; + $url_parts = parse_url($url); + $socket_host = $url_parts['host']; + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') + { + $socket_host = "ssl://$url_parts[host]"; + $url_parts['port'] = 443; + } + if (!isset($url_parts['port'])) + { + $url_parts['port'] = 80; + } + $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout); + if (!$fp) + { + $this->error = 'fsockopen error: ' . $errstr; + $this->success = false; + } + else + { + stream_set_timeout($fp, $timeout); + if (isset($url_parts['path'])) + { + if (isset($url_parts['query'])) + { + $get = "$url_parts[path]?$url_parts[query]"; + } + else + { + $get = $url_parts['path']; + } + } + else + { + $get = '/'; + } + $out = "GET $get HTTP/1.1\r\n"; + $out .= "Host: $url_parts[host]\r\n"; + $out .= "User-Agent: $useragent\r\n"; + if (extension_loaded('zlib')) + { + $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; + } - function get_bitrate() - { - if ($this->bitrate !== null) - { - return $this->bitrate; - } - else - { - return null; - } - } + if (isset($url_parts['user']) && isset($url_parts['pass'])) + { + $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; + } + foreach ($headers as $key => $value) + { + $out .= "$key: $value\r\n"; + } + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); - function get_caption($key = 0) - { - $captions = $this->get_captions(); - if (isset($captions[$key])) - { - return $captions[$key]; - } - else - { - return null; - } - } + $info = stream_get_meta_data($fp); - function get_captions() - { - if ($this->captions !== null) - { - return $this->captions; + $this->headers = ''; + while (!$info['eof'] && !$info['timed_out']) + { + $this->headers .= fread($fp, 1160); + $info = stream_get_meta_data($fp); + } + if (!$info['timed_out']) + { + $parser = new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + if (isset($this->headers['content-encoding'])) + { + // Hey, we act dumb elsewhere, so let's do that here too + switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) + { + case 'gzip': + case 'x-gzip': + $decoder = new SimplePie_gzdecode($this->body); + if (!$decoder->parse()) + { + $this->error = 'Unable to decode HTTP "gzip" stream'; + $this->success = false; + } + else + { + $this->body = $decoder->data; + } + break; + + case 'deflate': + if (($decompressed = gzinflate($this->body)) !== false) + { + $this->body = $decompressed; + } + else if (($decompressed = gzuncompress($this->body)) !== false) + { + $this->body = $decompressed; + } + else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) + { + $this->body = $decompressed; + } + else + { + $this->error = 'Unable to decode HTTP "deflate" stream'; + $this->success = false; + } + break; + + default: + $this->error = 'Unknown content coding'; + $this->success = false; + } + } + } + } + else + { + $this->error = 'fsocket timed out'; + $this->success = false; + } + fclose($fp); + } + } } else { - return null; + $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; + if (!$this->body = file_get_contents($url)) + { + $this->error = 'file_get_contents could not read the file'; + $this->success = false; + } } } +} - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } +/** + * Decode 'gzip' encoded HTTP data + * + * @package SimplePie + * @subpackage HTTP + * @link http://www.gzip.org/format.txt + */ +class SimplePie_gzdecode +{ + /** + * Compressed data + * + * @access private + * @var string + * @see gzdecode::$data + */ + var $compressed_data; - function get_categories() - { - if ($this->categories !== null) - { - return $this->categories; - } - else - { - return null; - } - } + /** + * Size of compressed data + * + * @access private + * @var int + */ + var $compressed_size; - function get_channels() - { - if ($this->channels !== null) - { - return $this->channels; - } - else - { - return null; - } - } + /** + * Minimum size of a valid gzip string + * + * @access private + * @var int + */ + var $min_compressed_size = 18; - function get_copyright() - { - if ($this->copyright !== null) - { - return $this->copyright; - } - else - { - return null; - } - } + /** + * Current position of pointer + * + * @access private + * @var int + */ + var $position = 0; - function get_credit($key = 0) - { - $credits = $this->get_credits(); - if (isset($credits[$key])) - { - return $credits[$key]; - } - else - { - return null; - } - } + /** + * Flags (FLG) + * + * @access private + * @var int + */ + var $flags; + + /** + * Uncompressed data + * + * @access public + * @see gzdecode::$compressed_data + * @var string + */ + var $data; + + /** + * Modified time + * + * @access public + * @var int + */ + var $MTIME; + + /** + * Extra Flags + * + * @access public + * @var int + */ + var $XFL; + + /** + * Operating System + * + * @access public + * @var int + */ + var $OS; + + /** + * Subfield ID 1 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI2 + * @var string + */ + var $SI1; + + /** + * Subfield ID 2 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI1 + * @var string + */ + var $SI2; + + /** + * Extra field content + * + * @access public + * @see gzdecode::$SI1 + * @see gzdecode::$SI2 + * @var string + */ + var $extra_field; + + /** + * Original filename + * + * @access public + * @var string + */ + var $filename; + + /** + * Human readable comment + * + * @access public + * @var string + */ + var $comment; - function get_credits() + /** + * Don't allow anything to be set + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) { - if ($this->credits !== null) - { - return $this->credits; - } - else - { - return null; - } + trigger_error("Cannot write property $name", E_USER_ERROR); } - function get_description() + /** + * Set the compressed string and related properties + * + * @param string $data + */ + public function __construct($data) { - if ($this->description !== null) - { - return $this->description; - } - else - { - return null; - } + $this->compressed_data = $data; + $this->compressed_size = strlen($data); } - function get_duration($convert = false) + /** + * Decode the GZIP stream + * + * @return bool Successfulness + */ + public function parse() { - if ($this->duration !== null) + if ($this->compressed_size >= $this->min_compressed_size) { - if ($convert) + // Check ID1, ID2, and CM + if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") { - $time = SimplePie_Misc::time_hms($this->duration); - return $time; + return false; } - else + + // Get the FLG (FLaGs) + $this->flags = ord($this->compressed_data[3]); + + // FLG bits above (1 << 4) are reserved + if ($this->flags > 0x1F) { - return $this->duration; + return false; } - } - else - { - return null; - } - } - function get_expression() - { - if ($this->expression !== null) - { - return $this->expression; - } - else - { - return 'full'; - } - } + // Advance the pointer after the above + $this->position += 4; - function get_extension() - { - if ($this->link !== null) - { - $url = SimplePie_Misc::parse_url($this->link); - if ($url['path'] !== '') + // MTIME + $mtime = substr($this->compressed_data, $this->position, 4); + // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness + if (current(unpack('S', "\x00\x01")) === 1) { - return pathinfo($url['path'], PATHINFO_EXTENSION); + $mtime = strrev($mtime); } - } - return null; - } + $this->MTIME = current(unpack('l', $mtime)); + $this->position += 4; - function get_framerate() - { - if ($this->framerate !== null) - { - return $this->framerate; + // Get the XFL (eXtra FLags) + $this->XFL = ord($this->compressed_data[$this->position++]); + + // Get the OS (Operating System) + $this->OS = ord($this->compressed_data[$this->position++]); + + // Parse the FEXTRA + if ($this->flags & 4) + { + // Read subfield IDs + $this->SI1 = $this->compressed_data[$this->position++]; + $this->SI2 = $this->compressed_data[$this->position++]; + + // SI2 set to zero is reserved for future use + if ($this->SI2 === "\x00") + { + return false; + } + + // Get the length of the extra field + $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + $this->position += 2; + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 4; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the extra field to the given data + $this->extra_field = substr($this->compressed_data, $this->position, $len); + $this->position += $len; + } + else + { + return false; + } + } + + // Parse the FNAME + if ($this->flags & 8) + { + // Get the length of the filename + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original filename to the given string + $this->filename = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FCOMMENT + if ($this->flags & 16) + { + // Get the length of the comment + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original comment to the given string + $this->comment = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FHCRC + if ($this->flags & 2) + { + // Check the length of the string is still valid + $this->min_compressed_size += $len + 2; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Read the CRC + $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + + // Check the CRC matches + if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) + { + $this->position += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + + // Decompress the actual data + if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) + { + return false; + } + else + { + $this->position = $this->compressed_size - 8; + } + + // Check CRC of data + $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) + { + return false; + }*/ + + // Check ISIZE of data + $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) + { + return false; + } + + // Wow, against all odds, we've actually got a valid gzip string + return true; } else { - return null; + return false; } } +} - function get_handler() - { - return $this->get_real_type(true); - } +/** + * HTTP Response Parser + * + * @package SimplePie + * @subpackage HTTP + */ +class SimplePie_HTTP_Parser +{ + /** + * HTTP Version + * + * @var float + */ + public $http_version = 0.0; + + /** + * Status code + * + * @var int + */ + public $status_code = 0; + + /** + * Reason phrase + * + * @var string + */ + public $reason = ''; + + /** + * Key/value pairs of the headers + * + * @var array + */ + public $headers = array(); + + /** + * Body of the response + * + * @var string + */ + public $body = ''; + + /** + * Current state of the state machine + * + * @var string + */ + protected $state = 'http_version'; + + /** + * Input data + * + * @var string + */ + protected $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @var int + */ + protected $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + */ + protected $position = 0; + + /** + * Name of the hedaer currently being parsed + * + * @var string + */ + protected $name = ''; + + /** + * Value of the hedaer currently being parsed + * + * @var string + */ + protected $value = ''; - function get_hash($key = 0) + /** + * Create an instance of the class with the input data + * + * @param string $data Input data + */ + public function __construct($data) { - $hashes = $this->get_hashes(); - if (isset($hashes[$key])) - { - return $hashes[$key]; - } - else - { - return null; - } + $this->data = $data; + $this->data_length = strlen($this->data); } - function get_hashes() + /** + * Parse the input data + * + * @return bool true on success, false on failure + */ + public function parse() { - if ($this->hashes !== null) - { - return $this->hashes; - } - else + while ($this->state && $this->state !== 'emit' && $this->has_data()) { - return null; + $state = $this->state; + $this->$state(); } - } - - function get_height() - { - if ($this->height !== null) + $this->data = ''; + if ($this->state === 'emit' || $this->state === 'body') { - return $this->height; + return true; } else { - return null; + $this->http_version = ''; + $this->status_code = ''; + $this->reason = ''; + $this->headers = array(); + $this->body = ''; + return false; } } - function get_language() - { - if ($this->lang !== null) - { - return $this->lang; - } - else - { - return null; - } + /** + * Check whether there is data beyond the pointer + * + * @return bool true if there is further data, false if not + */ + protected function has_data() + { + return (bool) ($this->position < $this->data_length); } - function get_keyword($key = 0) + /** + * See if the next character is LWS + * + * @return bool true if the next character is LWS, false if not + */ + protected function is_linear_whitespace() { - $keywords = $this->get_keywords(); - if (isset($keywords[$key])) - { - return $keywords[$key]; - } - else - { - return null; - } + return (bool) ($this->data[$this->position] === "\x09" + || $this->data[$this->position] === "\x20" + || ($this->data[$this->position] === "\x0A" + && isset($this->data[$this->position + 1]) + && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); } - function get_keywords() + /** + * Parse the HTTP version + */ + protected function http_version() { - if ($this->keywords !== null) + if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') { - return $this->keywords; + $len = strspn($this->data, '0123456789.', 5); + $this->http_version = substr($this->data, 5, $len); + $this->position += 5 + $len; + if (substr_count($this->http_version, '.') <= 1) + { + $this->http_version = (float) $this->http_version; + $this->position += strspn($this->data, "\x09\x20", $this->position); + $this->state = 'status'; + } + else + { + $this->state = false; + } } else { - return null; + $this->state = false; } } - function get_length() + /** + * Parse the status code + */ + protected function status() { - if ($this->length !== null) + if ($len = strspn($this->data, '0123456789', $this->position)) { - return $this->length; + $this->status_code = (int) substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'reason'; } else { - return null; + $this->state = false; } } - function get_link() + /** + * Parse the reason phrase + */ + protected function reason() { - if ($this->link !== null) - { - return urldecode($this->link); - } - else - { - return null; - } + $len = strcspn($this->data, "\x0A", $this->position); + $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); + $this->position += $len + 1; + $this->state = 'new_line'; } - function get_medium() + /** + * Deal with a new line, shifting data around as needed + */ + protected function new_line() { - if ($this->medium !== null) + $this->value = trim($this->value, "\x0D\x20"); + if ($this->name !== '' && $this->value !== '') { - return $this->medium; + $this->name = strtolower($this->name); + // We should only use the last Content-Type header. c.f. issue #1 + if (isset($this->headers[$this->name]) && $this->name !== 'content-type') + { + $this->headers[$this->name] .= ', ' . $this->value; + } + else + { + $this->headers[$this->name] = $this->value; + } } - else + $this->name = ''; + $this->value = ''; + if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") { - return null; + $this->position += 2; + $this->state = 'body'; } - } - - function get_player() - { - if ($this->player !== null) + elseif ($this->data[$this->position] === "\x0A") { - return $this->player; + $this->position++; + $this->state = 'body'; } else { - return null; + $this->state = 'name'; } } - function get_rating($key = 0) + /** + * Parse a header name + */ + protected function name() { - $ratings = $this->get_ratings(); - if (isset($ratings[$key])) + $len = strcspn($this->data, "\x0A:", $this->position); + if (isset($this->data[$this->position + $len])) { - return $ratings[$key]; + if ($this->data[$this->position + $len] === "\x0A") + { + $this->position += $len; + $this->state = 'new_line'; + } + else + { + $this->name = substr($this->data, $this->position, $len); + $this->position += $len + 1; + $this->state = 'value'; + } } else { - return null; + $this->state = false; } } - function get_ratings() + /** + * Parse LWS, replacing consecutive LWS characters with a single space + */ + protected function linear_whitespace() { - if ($this->ratings !== null) - { - return $this->ratings; - } - else + do { - return null; - } + if (substr($this->data, $this->position, 2) === "\x0D\x0A") + { + $this->position += 2; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + } + $this->position += strspn($this->data, "\x09\x20", $this->position); + } while ($this->has_data() && $this->is_linear_whitespace()); + $this->value .= "\x20"; } - function get_restriction($key = 0) + /** + * See what state to move to while within non-quoted header values + */ + protected function value() { - $restrictions = $this->get_restrictions(); - if (isset($restrictions[$key])) + if ($this->is_linear_whitespace()) { - return $restrictions[$key]; + $this->linear_whitespace(); } else { - return null; + switch ($this->data[$this->position]) + { + case '"': + // Workaround for ETags: we have to include the quotes as + // part of the tag. + if (strtolower($this->name) === 'etag') + { + $this->value .= '"'; + $this->position++; + $this->state = 'value_char'; + break; + } + $this->position++; + $this->state = 'quote'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + default: + $this->state = 'value_char'; + break; + } } } - function get_restrictions() + /** + * Parse a header value while outside quotes + */ + protected function value_char() { - if ($this->restrictions !== null) - { - return $this->restrictions; - } - else - { - return null; - } + $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; } - function get_sampling_rate() + /** + * See what state to move to while within quoted header values + */ + protected function quote() { - if ($this->samplingrate !== null) + if ($this->is_linear_whitespace()) { - return $this->samplingrate; + $this->linear_whitespace(); } else { - return null; + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'value'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + case '\\': + $this->position++; + $this->state = 'quote_escaped'; + break; + + default: + $this->state = 'quote_char'; + break; + } } } - function get_size() + /** + * Parse a header value while within quotes + */ + protected function quote_char() { - $length = $this->get_length(); - if ($length !== null) + $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * Parse an escaped character within quotes + */ + protected function quote_escaped() + { + $this->value .= $this->data[$this->position]; + $this->position++; + $this->state = 'quote'; + } + + /** + * Parse the body + */ + protected function body() + { + $this->body = substr($this->data, $this->position); + if (!empty($this->headers['transfer-encoding'])) { - return round($length/1048576, 2); + unset($this->headers['transfer-encoding']); + $this->state = 'chunked'; } else { - return null; + $this->state = 'emit'; } } - function get_thumbnail($key = 0) + /** + * Parsed a "Transfer-Encoding: chunked" body + */ + protected function chunked() { - $thumbnails = $this->get_thumbnails(); - if (isset($thumbnails[$key])) + if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) { - return $thumbnails[$key]; + $this->state = 'emit'; + return; } - else + + $decoded = ''; + $encoded = $this->body; + + while (true) { - return null; + $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); + if (!$is_chunked) + { + // Looks like it's not chunked after all + $this->state = 'emit'; + return; + } + + $length = hexdec(trim($matches[1])); + if ($length === 0) + { + // Ignore trailer headers + $this->state = 'emit'; + $this->body = $decoded; + return; + } + + $chunk_length = strlen($matches[0]); + $decoded .= $part = substr($encoded, $chunk_length, $length); + $encoded = substr($encoded, $chunk_length + $length + 2); + + if (trim($encoded) === '0' || empty($encoded)) + { + $this->state = 'emit'; + $this->body = $decoded; + return; + } } } +} + +/** + * IRI parser/serialiser/normaliser + * + * @package SimplePie + * @subpackage HTTP + * @author Geoffrey Sneddon + * @author Steve Minutillo + * @author Ryan McCue + * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue + * @license http://www.opensource.org/licenses/bsd-license.php + */ +class SimplePie_IRI +{ + /** + * Scheme + * + * @var string + */ + protected $scheme = null; + + /** + * User Information + * + * @var string + */ + protected $iuserinfo = null; + + /** + * ihost + * + * @var string + */ + protected $ihost = null; + + /** + * Port + * + * @var string + */ + protected $port = null; + + /** + * ipath + * + * @var string + */ + protected $ipath = ''; + + /** + * iquery + * + * @var string + */ + protected $iquery = null; + + /** + * ifragment + * + * @var string + */ + protected $ifragment = null; + + /** + * Normalization database + * + * Each key is the scheme, each value is an array with each key as the IRI + * part and value as the default value for that part. + */ + protected $normalization = array( + 'acap' => array( + 'port' => 674 + ), + 'dict' => array( + 'port' => 2628 + ), + 'file' => array( + 'ihost' => 'localhost' + ), + 'http' => array( + 'port' => 80, + 'ipath' => '/' + ), + 'https' => array( + 'port' => 443, + 'ipath' => '/' + ), + ); + + /** + * Return the entire IRI when you try and read the object as a string + * + * @return string + */ + public function __toString() + { + return $this->get_iri(); + } - function get_thumbnails() + /** + * Overload __set() to provide access via properties + * + * @param string $name Property name + * @param mixed $value Property value + */ + public function __set($name, $value) { - if ($this->thumbnails !== null) + if (method_exists($this, 'set_' . $name)) { - return $this->thumbnails; + call_user_func(array($this, 'set_' . $name), $value); } - else + elseif ( + $name === 'iauthority' + || $name === 'iuserinfo' + || $name === 'ihost' + || $name === 'ipath' + || $name === 'iquery' + || $name === 'ifragment' + ) { - return null; + call_user_func(array($this, 'set_' . substr($name, 1)), $value); } } - function get_title() + /** + * Overload __get() to provide access via properties + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) { - if ($this->title !== null) + // isset() returns false for null, we don't want to do that + // Also why we use array_key_exists below instead of isset() + $props = get_object_vars($this); + + if ( + $name === 'iri' || + $name === 'uri' || + $name === 'iauthority' || + $name === 'authority' + ) { - return $this->title; + $return = $this->{"get_$name"}(); + } + elseif (array_key_exists($name, $props)) + { + $return = $this->$name; + } + // host -> ihost + elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; + } + // ischeme -> scheme + elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; } else { - return null; + trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); + $return = null; } - } - function get_type() - { - if ($this->type !== null) + if ($return === null && isset($this->normalization[$this->scheme][$name])) { - return $this->type; + return $this->normalization[$this->scheme][$name]; } else { - return null; + return $return; } } - function get_width() + /** + * Overload __isset() to provide access via properties + * + * @param string $name Property name + * @return bool + */ + public function __isset($name) { - if ($this->width !== null) + if (method_exists($this, 'get_' . $name) || isset($this->$name)) { - return $this->width; + return true; } else { - return null; + return false; } } - function native_embed($options='') + /** + * Overload __unset() to provide access via properties + * + * @param string $name Property name + */ + public function __unset($name) { - return $this->embed($options, true); + if (method_exists($this, 'set_' . $name)) + { + call_user_func(array($this, 'set_' . $name), ''); + } } /** - * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. + * Create a new IRI object, from a specified string + * + * @param string $iri */ - function embed($options = '', $native = false) + public function __construct($iri = null) { - // Set up defaults - $audio = ''; - $video = ''; - $alt = ''; - $altclass = ''; - $loop = 'false'; - $width = 'auto'; - $height = 'auto'; - $bgcolor = '#ffffff'; - $mediaplayer = ''; - $widescreen = false; - $handler = $this->get_handler(); - $type = $this->get_real_type(); + $this->set_iri($iri); + } - // Process options and reassign values as necessary - if (is_array($options)) + /** + * Create a new IRI object by resolving a relative IRI + * + * Returns false if $base is not absolute, otherwise an IRI. + * + * @param IRI|string $base (Absolute) Base IRI + * @param IRI|string $relative Relative IRI + * @return IRI|false + */ + public static function absolutize($base, $relative) + { + if (!($relative instanceof SimplePie_IRI)) { - extract($options); + $relative = new SimplePie_IRI($relative); + } + if (!$relative->is_valid()) + { + return false; + } + elseif ($relative->scheme !== null) + { + return clone $relative; } else { - $options = explode(',', $options); - foreach($options as $option) + if (!($base instanceof SimplePie_IRI)) { - $opt = explode(':', $option, 2); - if (isset($opt[0], $opt[1])) - { - $opt[0] = trim($opt[0]); - $opt[1] = trim($opt[1]); - switch ($opt[0]) - { - case 'audio': - $audio = $opt[1]; - break; - - case 'video': - $video = $opt[1]; - break; - - case 'alt': - $alt = $opt[1]; - break; - - case 'altclass': - $altclass = $opt[1]; - break; - - case 'loop': - $loop = $opt[1]; - break; - - case 'width': - $width = $opt[1]; - break; - - case 'height': - $height = $opt[1]; - break; - - case 'bgcolor': - $bgcolor = $opt[1]; - break; - - case 'mediaplayer': - $mediaplayer = $opt[1]; - break; - - case 'widescreen': - $widescreen = $opt[1]; - break; - } - } + $base = new SimplePie_IRI($base); } - } - - $mime = explode('/', $type, 2); - $mime = $mime[0]; - - // Process values for 'auto' - if ($width === 'auto') - { - if ($mime === 'video') + if ($base->scheme !== null && $base->is_valid()) { - if ($height === 'auto') - { - $width = 480; - } - elseif ($widescreen) + if ($relative->get_iri() !== '') { - $width = round((intval($height)/9)*16); + if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) + { + $target = clone $relative; + $target->scheme = $base->scheme; + } + else + { + $target = new SimplePie_IRI; + $target->scheme = $base->scheme; + $target->iuserinfo = $base->iuserinfo; + $target->ihost = $base->ihost; + $target->port = $base->port; + if ($relative->ipath !== '') + { + if ($relative->ipath[0] === '/') + { + $target->ipath = $relative->ipath; + } + elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') + { + $target->ipath = '/' . $relative->ipath; + } + elseif (($last_segment = strrpos($base->ipath, '/')) !== false) + { + $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; + } + else + { + $target->ipath = $relative->ipath; + } + $target->ipath = $target->remove_dot_segments($target->ipath); + $target->iquery = $relative->iquery; + } + else + { + $target->ipath = $base->ipath; + if ($relative->iquery !== null) + { + $target->iquery = $relative->iquery; + } + elseif ($base->iquery !== null) + { + $target->iquery = $base->iquery; + } + } + $target->ifragment = $relative->ifragment; + } } else { - $width = round((intval($height)/3)*4); + $target = clone $base; + $target->ifragment = null; } + $target->scheme_normalization(); + return $target; } else { - $width = '100%'; + return false; } } + } - if ($height === 'auto') + /** + * Parse an IRI into scheme/authority/path/query/fragment segments + * + * @param string $iri + * @return array + */ + protected function parse_iri($iri) + { + $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); + if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match)) { - if ($mime === 'audio') + if ($match[1] === '') { - $height = 0; + $match['scheme'] = null; } - elseif ($mime === 'video') + if (!isset($match[3]) || $match[3] === '') { - if ($width === 'auto') - { - if ($widescreen) - { - $height = 270; - } - else - { - $height = 360; - } - } - elseif ($widescreen) - { - $height = round((intval($width)/16)*9); - } - else - { - $height = round((intval($width)/4)*3); - } + $match['authority'] = null; } - else + if (!isset($match[5])) { - $height = 376; + $match['path'] = ''; } + if (!isset($match[6]) || $match[6] === '') + { + $match['query'] = null; + } + if (!isset($match[8]) || $match[8] === '') + { + $match['fragment'] = null; + } + return $match; } - elseif ($mime === 'audio') - { - $height = 0; - } - - // Set proper placeholder value - if ($mime === 'audio') - { - $placeholder = $audio; - } - elseif ($mime === 'video') + else { - $placeholder = $video; + // This can occur when a paragraph is accidentally parsed as a URI + return false; } + } - $embed = ''; - - // Make sure the JS library is included - if (!$native) + /** + * Remove dot segments from a path + * + * @param string $input + * @return string + */ + protected function remove_dot_segments($input) + { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { - static $javascript_outputted = null; - if (!$javascript_outputted && $this->javascript) + // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) { - $embed .= '<script type="text/javascript" src="?' . htmlspecialchars($this->javascript) . '"></script>'; - $javascript_outputted = true; + $input = substr($input, 3); } - } - - // Odeo Feed MP3's - if ($handler === 'odeo') - { - if ($native) + elseif (strpos($input, './') === 0) { - $embed .= '<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://adobe.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url=' . $this->get_link() . '"></embed>'; + $input = substr($input, 2); } - else + // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) { - $embed .= '<script type="text/javascript">embed_odeo("' . $this->get_link() . '");</script>'; + $input = substr($input, 2); } - } - - // Flash - elseif ($handler === 'flash') - { - if ($native) + elseif ($input === '/.') { - $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>"; + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) + { + $input = substr($input, 3); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input === '/..') + { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, + elseif ($input === '.' || $input === '..') + { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) + { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); } else { - $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>"; + $output .= $input; + $input = ''; } } + return $output . $input; + } - // Flash Media Player file types. - // Preferred handler for MP3 file types. - elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) + /** + * Replace invalid character with percent encoding + * + * @param string $string Input string + * @param string $extra_chars Valid characters not in iunreserved or + * iprivate (this is ASCII-only) + * @param bool $iprivate Allow iprivate + * @return string + */ + protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) + { + // Normalize as many pct-encoded sections as possible + $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string); + + // Replace invalid percent characters + $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); + + // Add unreserved and % to $extra_chars (the latter is safe because all + // pct-encoded sections are now valid). + $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; + + // Now replace any bytes that aren't allowed with their pct-encoded versions + $position = 0; + $strlen = strlen($string); + while (($position += strspn($string, $extra_chars, $position)) < $strlen) { - $height += 20; - if ($native) + $value = ord($string[$position]); + + // Start position + $start = $position; + + // By default we are valid + $valid = true; + + // No one byte sequences are valid due to the while. + // Two byte sequence: + if (($value & 0xE0) === 0xC0) { - $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>"; + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; } + // Invalid byte: else { - $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>"; + $valid = false; + $length = 1; + $remaining = 0; } - } - // QuickTime 7 file types. Need to test with QuickTime 6. - // Only handle MP3's if the Flash Media Player is not present. - elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) - { - $height += 16; - if ($native) + if ($remaining) { - if ($placeholder !== '') + if ($position + $length <= $strlen) { - $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + for ($position++; $remaining; $position++) + { + $value = ord($string[$position]); + + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $character |= ($value & 0x3F) << (--$remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte: + else + { + $valid = false; + $position--; + break; + } + } } else { - $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + $position = $strlen - 1; + $valid = false; } } - else - { - $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>"; - } - } - // Windows Media - elseif ($handler === 'wmedia') - { - $height += 45; - if ($native) - { - $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>"; - } - else + // Percent encode anything invalid or not in ucschar + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0xA0 + || $character > 0xEFFFD + ) + && ( + // Everything not in iprivate, if it applies + !$iprivate + || $character < 0xE000 + || $character > 0x10FFFD + ) + ) { - $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>"; + // If we were a character, pretend we weren't, but rather an error. + if ($valid) + $position--; + + for ($j = $start; $j <= $position; $j++) + { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); + $j += 2; + $position += 2; + $strlen += 2; + } } } - // Everything else - else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>'; - - return $embed; + return $string; } - function get_real_type($find_handler = false) + /** + * Callback function for preg_replace_callback. + * + * Removes sequences of percent encoded bytes that represent UTF-8 + * encoded characters in iunreserved + * + * @param array $match PCRE match + * @return string Replacement + */ + protected function remove_iunreserved_percent_encoded($match) { - // If it's Odeo, let's get it out of the way. - if (substr(strtolower($this->get_link()), 0, 15) === 'http://odeo.com') - { - return 'odeo'; - } - - // Mime-types by handler. - $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash - $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player - $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime - $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media - $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 + // As we just have valid percent encoded sequences we can just explode + // and ignore the first member of the returned array (an empty string). + $bytes = explode('%', $match[0]); - if ($this->get_type() !== null) - { - $type = strtolower($this->type); - } - else - { - $type = null; - } + // Initialize the new string (this is what will be returned) and that + // there are no bytes remaining in the current sequence (unsurprising + // at the first byte!). + $string = ''; + $remaining = 0; - // If we encounter an unsupported mime-type, check the file extension and guess intelligently. - if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) + // Loop over each and every byte, and set $value to its value + for ($i = 1, $len = count($bytes); $i < $len; $i++) { - switch (strtolower($this->get_extension())) - { - // Audio mime-types - case 'aac': - case 'adts': - $type = 'audio/acc'; - break; - - case 'aif': - case 'aifc': - case 'aiff': - case 'cdda': - $type = 'audio/aiff'; - break; - - case 'bwf': - $type = 'audio/wav'; - break; + $value = hexdec($bytes[$i]); - case 'kar': - case 'mid': - case 'midi': - case 'smf': - $type = 'audio/midi'; - break; + // If we're the first byte of sequence: + if (!$remaining) + { + // Start position + $start = $i; - case 'm4a': - $type = 'audio/x-m4a'; - break; + // By default we are valid + $valid = true; - case 'mp3': - case 'swa': - $type = 'audio/mp3'; - break; + // One byte sequence: + if ($value <= 0x7F) + { + $character = $value; + $length = 1; + } + // Two byte sequence: + elseif (($value & 0xE0) === 0xC0) + { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else + { + $valid = false; + $remaining = 0; + } + } + // Continuation byte: + else + { + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $remaining--; + $character |= ($value & 0x3F) << ($remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: + else + { + $valid = false; + $remaining = 0; + $i--; + } + } - case 'wav': - $type = 'audio/wav'; - break; + // If we've reached the end of the current byte sequence, append it to Unicode::$data + if (!$remaining) + { + // Percent encode anything invalid or not in iunreserved + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of iunreserved codepoints + || $character < 0x2D + || $character > 0xEFFFD + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + // Everything else not in iunreserved (this is all BMP) + || $character === 0x2F + || $character > 0x39 && $character < 0x41 + || $character > 0x5A && $character < 0x61 + || $character > 0x7A && $character < 0x7E + || $character > 0x7E && $character < 0xA0 + || $character > 0xD7FF && $character < 0xF900 + ) + { + for ($j = $start; $j <= $i; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } + else + { + for ($j = $start; $j <= $i; $j++) + { + $string .= chr(hexdec($bytes[$j])); + } + } + } + } - case 'wax': - $type = 'audio/x-ms-wax'; - break; + // If we have any bytes left over they are invalid (i.e., we are + // mid-way through a multi-byte sequence) + if ($remaining) + { + for ($j = $start; $j < $len; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } - case 'wma': - $type = 'audio/x-ms-wma'; - break; + return $string; + } - // Video mime-types - case '3gp': - case '3gpp': - $type = 'video/3gpp'; - break; + protected function scheme_normalization() + { + if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) + { + $this->iuserinfo = null; + } + if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) + { + $this->ihost = null; + } + if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) + { + $this->port = null; + } + if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) + { + $this->ipath = ''; + } + if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) + { + $this->iquery = null; + } + if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) + { + $this->ifragment = null; + } + } - case '3g2': - case '3gp2': - $type = 'video/3gpp2'; - break; + /** + * Check if the object represents a valid IRI. This needs to be done on each + * call as some things change depending on another part of the IRI. + * + * @return bool + */ + public function is_valid() + { + $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; + if ($this->ipath !== '' && + ( + $isauthority && ( + $this->ipath[0] !== '/' || + substr($this->ipath, 0, 2) === '//' + ) || + ( + $this->scheme === null && + !$isauthority && + strpos($this->ipath, ':') !== false && + (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) + ) + ) + ) + { + return false; + } - case 'asf': - $type = 'video/x-ms-asf'; - break; + return true; + } - case 'flv': - $type = 'video/x-flv'; - break; + /** + * Set the entire IRI. Returns true on success, false on failure (if there + * are any invalid characters). + * + * @param string $iri + * @return bool + */ + public function set_iri($iri) + { + static $cache; + if (!$cache) + { + $cache = array(); + } - case 'm1a': - case 'm1s': - case 'm1v': - case 'm15': - case 'm75': - case 'mp2': - case 'mpa': - case 'mpeg': - case 'mpg': - case 'mpm': - case 'mpv': - $type = 'video/mpeg'; - break; + if ($iri === null) + { + return true; + } + elseif (isset($cache[$iri])) + { + list($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return) = $cache[$iri]; + return $return; + } + else + { + $parsed = $this->parse_iri((string) $iri); + if (!$parsed) + { + return false; + } - case 'm4v': - $type = 'video/x-m4v'; - break; + $return = $this->set_scheme($parsed['scheme']) + && $this->set_authority($parsed['authority']) + && $this->set_path($parsed['path']) + && $this->set_query($parsed['query']) + && $this->set_fragment($parsed['fragment']); + + $cache[$iri] = array($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return); + return $return; + } + } - case 'mov': - case 'qt': - $type = 'video/quicktime'; - break; - - case 'mp4': - case 'mpg4': - $type = 'video/mp4'; - break; - - case 'sdv': - $type = 'video/sd-video'; - break; - - case 'wm': - $type = 'video/x-ms-wm'; - break; - - case 'wmv': - $type = 'video/x-ms-wmv'; - break; - - case 'wvx': - $type = 'video/x-ms-wvx'; - break; + /** + * Set the scheme. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $scheme + * @return bool + */ + public function set_scheme($scheme) + { + if ($scheme === null) + { + $this->scheme = null; + } + elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) + { + $this->scheme = null; + return false; + } + else + { + $this->scheme = strtolower($scheme); + } + return true; + } - // Flash mime-types - case 'spl': - $type = 'application/futuresplash'; - break; + /** + * Set the authority. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $authority + * @return bool + */ + public function set_authority($authority) + { + static $cache; + if (!$cache) + $cache = array(); - case 'swf': - $type = 'application/x-shockwave-flash'; - break; - } + if ($authority === null) + { + $this->iuserinfo = null; + $this->ihost = null; + $this->port = null; + return true; } + elseif (isset($cache[$authority])) + { + list($this->iuserinfo, + $this->ihost, + $this->port, + $return) = $cache[$authority]; - if ($find_handler) + return $return; + } + else { - if (in_array($type, $types_flash)) - { - return 'flash'; - } - elseif (in_array($type, $types_fmedia)) - { - return 'fmedia'; - } - elseif (in_array($type, $types_quicktime)) + $remaining = $authority; + if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { - return 'quicktime'; + $iuserinfo = substr($remaining, 0, $iuserinfo_end); + $remaining = substr($remaining, $iuserinfo_end + 1); } - elseif (in_array($type, $types_wmedia)) + else { - return 'wmedia'; + $iuserinfo = null; } - elseif (in_array($type, $types_mp3)) + if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { - return 'mp3'; + if (($port = substr($remaining, $port_start + 1)) === false) + { + $port = null; + } + $remaining = substr($remaining, 0, $port_start); } else { - return null; + $port = null; } - } - else - { - return $type; - } - } -} -class SimplePie_Caption -{ - var $type; - var $lang; - var $startTime; - var $endTime; - var $text; + $return = $this->set_userinfo($iuserinfo) && + $this->set_host($remaining) && + $this->set_port($port); - // Constructor, used to input the data - function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) - { - $this->type = $type; - $this->lang = $lang; - $this->startTime = $startTime; - $this->endTime = $endTime; - $this->text = $text; - } + $cache[$authority] = array($this->iuserinfo, + $this->ihost, + $this->port, + $return); - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); + return $return; + } } - function get_endtime() + /** + * Set the iuserinfo. + * + * @param string $iuserinfo + * @return bool + */ + public function set_userinfo($iuserinfo) { - if ($this->endTime !== null) + if ($iuserinfo === null) { - return $this->endTime; + $this->iuserinfo = null; } else { - return null; + $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); + $this->scheme_normalization(); } + + return true; } - function get_language() + /** + * Set the ihost. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $ihost + * @return bool + */ + public function set_host($ihost) { - if ($this->lang !== null) + if ($ihost === null) { - return $this->lang; + $this->ihost = null; + return true; + } + elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') + { + if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1))) + { + $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']'; + } + else + { + $this->ihost = null; + return false; + } } else { - return null; + $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); + + // Lowercase, but ignore pct-encoded sections (as they should + // remain uppercase). This must be done after the previous step + // as that can add unescaped characters. + $position = 0; + $strlen = strlen($ihost); + while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) + { + if ($ihost[$position] === '%') + { + $position += 3; + } + else + { + $ihost[$position] = strtolower($ihost[$position]); + $position++; + } + } + + $this->ihost = $ihost; } + + $this->scheme_normalization(); + + return true; } - function get_starttime() + /** + * Set the port. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $port + * @return bool + */ + public function set_port($port) { - if ($this->startTime !== null) + if ($port === null) { - return $this->startTime; + $this->port = null; + return true; + } + elseif (strspn($port, '0123456789') === strlen($port)) + { + $this->port = (int) $port; + $this->scheme_normalization(); + return true; } else { - return null; + $this->port = null; + return false; } } - function get_text() + /** + * Set the ipath. + * + * @param string $ipath + * @return bool + */ + public function set_path($ipath) { - if ($this->text !== null) - { - return $this->text; - } - else + static $cache; + if (!$cache) { - return null; + $cache = array(); } - } - function get_type() - { - if ($this->type !== null) + $ipath = (string) $ipath; + + if (isset($cache[$ipath])) { - return $this->type; + $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; } else { - return null; - } - } -} + $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); + $removed = $this->remove_dot_segments($valid); -class SimplePie_Credit -{ - var $role; - var $scheme; - var $name; - - // Constructor, used to input the data - function SimplePie_Credit($role = null, $scheme = null, $name = null) - { - $this->role = $role; - $this->scheme = $scheme; - $this->name = $name; - } + $cache[$ipath] = array($valid, $removed); + $this->ipath = ($this->scheme !== null) ? $removed : $valid; + } - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); + $this->scheme_normalization(); + return true; } - function get_role() + /** + * Set the iquery. + * + * @param string $iquery + * @return bool + */ + public function set_query($iquery) { - if ($this->role !== null) + if ($iquery === null) { - return $this->role; + $this->iquery = null; } else { - return null; + $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); + $this->scheme_normalization(); } + return true; } - function get_scheme() + /** + * Set the ifragment. + * + * @param string $ifragment + * @return bool + */ + public function set_fragment($ifragment) { - if ($this->scheme !== null) + if ($ifragment === null) { - return $this->scheme; + $this->ifragment = null; } else { - return null; + $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); + $this->scheme_normalization(); } + return true; } - function get_name() + /** + * Convert an IRI to a URI (or parts thereof) + * + * @return string + */ + public function to_uri($string) { - if ($this->name !== null) + static $non_ascii; + if (!$non_ascii) { - return $this->name; + $non_ascii = implode('', range("\x80", "\xFF")); } - else + + $position = 0; + $strlen = strlen($string); + while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { - return null; + $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); + $position += 3; + $strlen += 2; } - } -} - -class SimplePie_Copyright -{ - var $url; - var $label; - - // Constructor, used to input the data - function SimplePie_Copyright($url = null, $label = null) - { - $this->url = $url; - $this->label = $label; - } - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); + return $string; } - function get_url() + /** + * Get the complete IRI + * + * @return string + */ + public function get_iri() { - if ($this->url !== null) + if (!$this->is_valid()) { - return $this->url; + return false; } - else + + $iri = ''; + if ($this->scheme !== null) { - return null; + $iri .= $this->scheme . ':'; } - } - - function get_attribution() - { - if ($this->label !== null) + if (($iauthority = $this->get_iauthority()) !== null) { - return $this->label; + $iri .= '//' . $iauthority; } - else + if ($this->ipath !== '') { - return null; + $iri .= $this->ipath; + } + elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '') + { + $iri .= $this->normalization[$this->scheme]['ipath']; + } + if ($this->iquery !== null) + { + $iri .= '?' . $this->iquery; + } + if ($this->ifragment !== null) + { + $iri .= '#' . $this->ifragment; } - } -} - -class SimplePie_Rating -{ - var $scheme; - var $value; - // Constructor, used to input the data - function SimplePie_Rating($scheme = null, $value = null) - { - $this->scheme = $scheme; - $this->value = $value; + return $iri; } - function __toString() + /** + * Get the complete URI + * + * @return string + */ + public function get_uri() { - // There is no $this->data here - return md5(serialize($this)); + return $this->to_uri($this->get_iri()); } - function get_scheme() + /** + * Get the complete iauthority + * + * @return string + */ + protected function get_iauthority() { - if ($this->scheme !== null) + if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) { - return $this->scheme; + $iauthority = ''; + if ($this->iuserinfo !== null) + { + $iauthority .= $this->iuserinfo . '@'; + } + if ($this->ihost !== null) + { + $iauthority .= $this->ihost; + } + if ($this->port !== null) + { + $iauthority .= ':' . $this->port; + } + return $iauthority; } else { @@ -7569,56 +8996,125 @@ class SimplePie_Rating } } - function get_value() + /** + * Get the complete authority + * + * @return string + */ + protected function get_authority() { - if ($this->value !== null) - { - return $this->value; - } + $iauthority = $this->get_iauthority(); + if (is_string($iauthority)) + return $this->to_uri($iauthority); else - { - return null; - } + return $iauthority; } } -class SimplePie_Restriction +/** + * Manages all item-related data + * + * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()} + * + * This class can be overloaded with {@see SimplePie::set_item_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Item { - var $relationship; - var $type; - var $value; + /** + * Parent feed + * + * @access private + * @var SimplePie + */ + var $feed; + + /** + * Raw data + * + * @access private + * @var array + */ + var $data = array(); + + /** + * Registry object + * + * @see set_registry + * @var SimplePie_Registry + */ + protected $registry; - // Constructor, used to input the data - function SimplePie_Restriction($relationship = null, $type = null, $value = null) + /** + * Create a new item object + * + * This is usually used by {@see SimplePie::get_items} and + * {@see SimplePie::get_item}. Avoid creating this manually. + * + * @param SimplePie $feed Parent feed + * @param array $data Raw data + */ + public function __construct($feed, $data) { - $this->relationship = $relationship; - $this->type = $type; - $this->value = $value; + $this->feed = $feed; + $this->data = $data; } - function __toString() + /** + * Set the registry handler + * + * This is usually used by {@see SimplePie_Registry::create} + * + * @since 1.3 + * @param SimplePie_Registry $registry + */ + public function set_registry(SimplePie_Registry $registry) { - // There is no $this->data here - return md5(serialize($this)); + $this->registry = $registry; } - function get_relationship() + /** + * Get a string representation of the item + * + * @return string + */ + public function __toString() { - if ($this->relationship !== null) - { - return $this->relationship; - } - else + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + public function __destruct() + { + if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) { - return null; + unset($this->feed); } } - function get_type() + /** + * Get data for an item-level element + * + * This method allows you to get access to ANY element/attribute that is a + * sub-element of the item/entry tag. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_item_tags($namespace, $tag) { - if ($this->type !== null) + if (isset($this->data['child'][$namespace][$tag])) { - return $this->type; + return $this->data['child'][$namespace][$tag]; } else { @@ -7626,7674 +9122,8647 @@ class SimplePie_Restriction } } - function get_value() + /** + * Get the base URL value from the parent feed + * + * Uses `<xml:base>` + * + * @param array $element + * @return string + */ + public function get_base($element = array()) { - if ($this->value !== null) + return $this->feed->get_base($element); + } + + /** + * Sanitize feed data + * + * @access private + * @see SimplePie::sanitize() + * @param string $data Data to sanitize + * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants + * @param string $base Base URL to resolve URLs against + * @return string Sanitized data + */ + public function sanitize($data, $type, $base = '') + { + return $this->feed->sanitize($data, $type, $base); + } + + /** + * Get the parent feed + * + * Note: this may not work as you think for multifeeds! + * + * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed + * @since 1.0 + * @return SimplePie + */ + public function get_feed() + { + return $this->feed; + } + + /** + * Get the unique identifier for the item + * + * This is usually used when writing code to check for new items in a feed. + * + * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute + * for RDF. If none of these are supplied (or `$hash` is true), creates an + * MD5 hash based on the permalink and title. If either of those are not + * supplied, creates a hash based on the full feed data. + * + * @since Beta 2 + * @param boolean $hash Should we force using a hash instead of the supplied ID? + * @return string + */ + public function get_id($hash = false) + { + if (!$hash) { - return $this->value; + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'])) + { + return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($return = $this->get_permalink()) !== null) + { + return $return; + } + elseif (($return = $this->get_title()) !== null) + { + return $return; + } + } + if ($this->get_permalink() !== null || $this->get_title() !== null) + { + return md5($this->get_permalink() . $this->get_title()); } else { - return null; + return md5(serialize($this->data)); } } -} - -/** - * @todo Move to properly supporting RFC2616 (HTTP/1.1) - */ -class SimplePie_File -{ - var $url; - var $useragent; - var $success = true; - var $headers = array(); - var $body; - var $status_code; - var $redirects = 0; - var $error; - var $method = SIMPLEPIE_FILE_SOURCE_NONE; - function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) + /** + * Get the title of the item + * + * Uses `<atom:title>`, `<title>` or `<dc:title>` + * + * @since Beta 2 (previously called `get_item_title` since 0.8) + * @return string|null + */ + public function get_title() { - if (class_exists('idna_convert')) - { - $idn = new idna_convert; - $parsed = SimplePie_Misc::parse_url($url); - $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); - } - $this->url = $url; - $this->useragent = $useragent; - if (preg_match('/^http(s)?:\/\//i', $url)) + if (!isset($this->data['title'])) { - if ($useragent === null) + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { - $useragent = ini_get('user_agent'); - $this->useragent = $useragent; + $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } - if (!is_array($headers)) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) { - $headers = array(); + $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } - if (!$force_fsockopen && function_exists('curl_exec')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; - $fp = curl_init(); - $headers2 = array(); - foreach ($headers as $key => $value) - { - $headers2[] = "$key: $value"; - } - if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) - { - curl_setopt($fp, CURLOPT_ENCODING, ''); - } - curl_setopt($fp, CURLOPT_URL, $url); - curl_setopt($fp, CURLOPT_HEADER, 1); - curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_REFERER, $url); - curl_setopt($fp, CURLOPT_USERAGENT, $useragent); - curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); - if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) - { - curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); - } - - $this->headers = curl_exec($fp); - if (curl_errno($fp) === 23 || curl_errno($fp) === 61) - { - curl_setopt($fp, CURLOPT_ENCODING, 'none'); - $this->headers = curl_exec($fp); - } - if (curl_errno($fp)) - { - $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); - $this->success = false; - } - else - { - $info = curl_getinfo($fp); - curl_close($fp); - $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); - $this->headers = array_pop($this->headers); - $parser = new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - } - } + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } - else + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; - $url_parts = parse_url($url); - $socket_host = $url_parts['host']; - if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') - { - $socket_host = "ssl://$url_parts[host]"; - $url_parts['port'] = 443; - } - if (!isset($url_parts['port'])) - { - $url_parts['port'] = 80; - } - $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout); - if (!$fp) - { - $this->error = 'fsockopen error: ' . $errstr; - $this->success = false; - } - else - { - stream_set_timeout($fp, $timeout); - if (isset($url_parts['path'])) - { - if (isset($url_parts['query'])) - { - $get = "$url_parts[path]?$url_parts[query]"; - } - else - { - $get = $url_parts['path']; - } - } - else - { - $get = '/'; - } - $out = "GET $get HTTP/1.0\r\n"; - $out .= "Host: $url_parts[host]\r\n"; - $out .= "User-Agent: $useragent\r\n"; - if (extension_loaded('zlib')) - { - $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; - } - - if (isset($url_parts['user']) && isset($url_parts['pass'])) - { - $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; - } - foreach ($headers as $key => $value) - { - $out .= "$key: $value\r\n"; - } - $out .= "Connection: Close\r\n\r\n"; - fwrite($fp, $out); - - $info = stream_get_meta_data($fp); - - $this->headers = ''; - while (!$info['eof'] && !$info['timed_out']) - { - $this->headers .= fread($fp, 1160); - $info = stream_get_meta_data($fp); - } - if (!$info['timed_out']) - { - $parser = new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - if (isset($this->headers['content-encoding'])) - { - // Hey, we act dumb elsewhere, so let's do that here too - switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) - { - case 'gzip': - case 'x-gzip': - $decoder = new SimplePie_gzdecode($this->body); - if (!$decoder->parse()) - { - $this->error = 'Unable to decode HTTP "gzip" stream'; - $this->success = false; - } - else - { - $this->body = $decoder->data; - } - break; - - case 'deflate': - if (($body = gzuncompress($this->body)) === false) - { - if (($body = gzinflate($this->body)) === false) - { - $this->error = 'Unable to decode HTTP "deflate" stream'; - $this->success = false; - } - } - $this->body = $body; - break; - - default: - $this->error = 'Unknown content coding'; - $this->success = false; - } - } - } - } - else - { - $this->error = 'fsocket timed out'; - $this->success = false; - } - fclose($fp); - } + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } - } - else - { - $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; - if (!$this->body = file_get_contents($url)) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) { - $this->error = 'file_get_contents could not read the file'; - $this->success = false; + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $this->data['title'] = null; } } + return $this->data['title']; } -} -/** - * HTTP Response Parser - * - * @package SimplePie - */ -class SimplePie_HTTP_Parser -{ /** - * HTTP Version + * Get the content for the item * - * @access public - * @var float - */ - var $http_version = 0.0; - - /** - * Status code + * Prefers summaries over full content , but will return full content if a + * summary does not exist. * - * @access public - * @var int - */ - var $status_code = 0; - - /** - * Reason phrase + * To prefer full content instead, use {@see get_content} * - * @access public - * @var string - */ - var $reason = ''; - - /** - * Key/value pairs of the headers + * Uses `<atom:summary>`, `<description>`, `<dc:description>` or + * `<itunes:subtitle>` * - * @access public - * @var array + * @since 0.8 + * @param boolean $description_only Should we avoid falling back to the content? + * @return string|null */ - var $headers = array(); + public function get_description($description_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML); + } + + elseif (!$description_only) + { + return $this->get_content(true); + } + else + { + return null; + } + } /** - * Body of the response - * - * @access public - * @var string - */ - var $body = ''; - - /** - * Current state of the state machine - * - * @access private - * @var string - */ - var $state = 'http_version'; - - /** - * Input data - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Input data length (to avoid calling strlen() everytime this is needed) - * - * @access private - * @var int - */ - var $data_length = 0; - - /** - * Current position of the pointer - * - * @var int - * @access private - */ - var $position = 0; - - /** - * Name of the hedaer currently being parsed + * Get the content for the item * - * @access private - * @var string - */ - var $name = ''; - - /** - * Value of the hedaer currently being parsed + * Prefers full content over summaries, but will return a summary if full + * content does not exist. * - * @access private - * @var string - */ - var $value = ''; - - /** - * Create an instance of the class with the input data + * To prefer summaries instead, use {@see get_description} * - * @access public - * @param string $data Input data - */ - function SimplePie_HTTP_Parser($data) - { - $this->data = $data; - $this->data_length = strlen($this->data); - } - - /** - * Parse the input data + * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module) * - * @access public - * @return bool true on success, false on failure + * @since 1.0 + * @param boolean $content_only Should we avoid falling back to the description? + * @return string|null */ - function parse() + public function get_content($content_only = false) { - while ($this->state && $this->state !== 'emit' && $this->has_data()) + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) { - $state = $this->state; - $this->$state(); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } - $this->data = ''; - if ($this->state === 'emit' || $this->state === 'body') + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) { - return true; + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif (!$content_only) + { + return $this->get_description(true); } else { - $this->http_version = ''; - $this->status_code = ''; - $this->reason = ''; - $this->headers = array(); - $this->body = ''; - return false; + return null; } } /** - * Check whether there is data beyond the pointer + * Get a category for the item * - * @access private - * @return bool true if there is further data, false if not + * @since Beta 3 (previously called `get_categories()` since Beta 2) + * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Category|null */ - function has_data() + public function get_category($key = 0) { - return (bool) ($this->position < $this->data_length); + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } } /** - * See if the next character is LWS + * Get all categories for the item * - * @access private - * @return bool true if the next character is LWS, false if not - */ - function is_linear_whitespace() - { - return (bool) ($this->data[$this->position] === "\x09" - || $this->data[$this->position] === "\x20" - || ($this->data[$this->position] === "\x0A" - && isset($this->data[$this->position + 1]) - && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); - } - - /** - * Parse the HTTP version + * Uses `<atom:category>`, `<category>` or `<dc:subject>` * - * @access private + * @since Beta 3 + * @return array|null List of {@see SimplePie_Category} objects */ - function http_version() + public function get_categories() { - if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') + $categories = array(); + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) { - $len = strspn($this->data, '0123456789.', 5); - $this->http_version = substr($this->data, 5, $len); - $this->position += 5 + $len; - if (substr_count($this->http_version, '.') <= 1) + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) { - $this->http_version = (float) $this->http_version; - $this->position += strspn($this->data, "\x09\x20", $this->position); - $this->state = 'status'; + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + { + // This is really the label, but keep this as the term also for BC. + // Label will also work on retrieving because that falls back to term. + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($category['attribs']['']['domain'])) + { + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); } else { - $this->state = false; + $scheme = null; } + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($categories)) + { + return array_unique($categories); } else { - $this->state = false; + return null; } } /** - * Parse the status code + * Get an author for the item * - * @access private + * @since Beta 2 + * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null */ - function status() + public function get_author($key = 0) { - if ($len = strspn($this->data, '0123456789', $this->position)) + $authors = $this->get_authors(); + if (isset($authors[$key])) { - $this->status_code = (int) substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'reason'; + return $authors[$key]; } else { - $this->state = false; + return null; } } /** - * Parse the reason phrase + * Get a contributor for the item * - * @access private + * @since 1.1 + * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null */ - function reason() + public function get_contributor($key = 0) { - $len = strcspn($this->data, "\x0A", $this->position); - $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); - $this->position += $len + 1; - $this->state = 'new_line'; + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } } /** - * Deal with a new line, shifting data around as needed + * Get all contributors for the item * - * @access private + * Uses `<atom:contributor>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects */ - function new_line() + public function get_contributors() { - $this->value = trim($this->value, "\x0D\x20"); - if ($this->name !== '' && $this->value !== '') + $contributors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) { - $this->name = strtolower($this->name); - if (isset($this->headers[$this->name])) + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { - $this->headers[$this->name] .= ', ' . $this->value; + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - else + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { - $this->headers[$this->name] = $this->value; + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); } } - $this->name = ''; - $this->value = ''; - if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) { - $this->position += 2; - $this->state = 'body'; + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); + } } - elseif ($this->data[$this->position] === "\x0A") + + if (!empty($contributors)) { - $this->position++; - $this->state = 'body'; + return array_unique($contributors); } else { - $this->state = 'name'; + return null; } } /** - * Parse a header name + * Get all authors for the item * - * @access private + * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` + * + * @since Beta 2 + * @return array|null List of {@see SimplePie_Author} objects */ - function name() + public function get_authors() { - $len = strcspn($this->data, "\x0A:", $this->position); - if (isset($this->data[$this->position + $len])) + $authors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) { - if ($this->data[$this->position + $len] === "\x0A") - { - $this->position += $len; - $this->state = 'new_line'; + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - else + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { - $this->name = substr($this->data, $this->position, $len); - $this->position += $len + 1; - $this->state = 'value'; + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) + { + $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($authors)) + { + return array_unique($authors); + } + elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) + { + return $authors; + } + elseif ($authors = $this->feed->get_authors()) + { + return $authors; + } else { - $this->state = false; + return null; } } /** - * Parse LWS, replacing consecutive LWS characters with a single space + * Get the copyright info for the item * - * @access private + * Uses `<atom:rights>` or `<dc:rights>` + * + * @since 1.1 + * @return string */ - function linear_whitespace() + public function get_copyright() { - do + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) { - if (substr($this->data, $this->position, 2) === "\x0D\x0A") - { - $this->position += 2; - } - elseif ($this->data[$this->position] === "\x0A") - { - $this->position++; - } - $this->position += strspn($this->data, "\x09\x20", $this->position); - } while ($this->has_data() && $this->is_linear_whitespace()); - $this->value .= "\x20"; + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } } /** - * See what state to move to while within non-quoted header values + * Get the posting date/time for the item * - * @access private + * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`, + * `<atom:modified>`, `<pubDate>` or `<dc:date>` + * + * Note: obeys PHP's timezone setting. To get a UTC date/time, use + * {@see get_gmdate} + * + * @since Beta 2 (previously called `get_item_date` since 0.8) + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) + * @return int|string|null */ - function value() + public function get_date($date_format = 'j F Y, g:i a') { - if ($this->is_linear_whitespace()) + if (!isset($this->data['date'])) { - $this->linear_whitespace(); + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['date']['raw'])) + { + $parser = $this->registry->call('Parse_Date', 'get'); + $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['date'] = null; + } } - else + if ($this->data['date']) { - switch ($this->data[$this->position]) + $date_format = (string) $date_format; + switch ($date_format) { - case '"': - $this->position++; - $this->state = 'quote'; - break; + case '': + return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; + case 'U': + return $this->data['date']['parsed']; default: - $this->state = 'value_char'; - break; + return date($date_format, $this->data['date']['parsed']); } } + else + { + return null; + } } /** - * Parse a header value while outside quotes + * Get the update date/time for the item * - * @access private - */ - function value_char() - { - $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; - } - - /** - * See what state to move to while within quoted header values + * Uses `<atom:updated>` * - * @access private + * Note: obeys PHP's timezone setting. To get a UTC date/time, use + * {@see get_gmdate} + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) + * @return int|string|null */ - function quote() + public function get_updated_date($date_format = 'j F Y, g:i a') { - if ($this->is_linear_whitespace()) + if (!isset($this->data['updated'])) { - $this->linear_whitespace(); + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['updated']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['updated']['raw'])) + { + $parser = $this->registry->call('Parse_Date', 'get'); + $this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['updated'] = null; + } } - else + if ($this->data['updated']) { - switch ($this->data[$this->position]) + $date_format = (string) $date_format; + switch ($date_format) { - case '"': - $this->position++; - $this->state = 'value'; - break; - - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; + case '': + return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); - case '\\': - $this->position++; - $this->state = 'quote_escaped'; - break; + case 'U': + return $this->data['updated']['parsed']; default: - $this->state = 'quote_char'; - break; + return date($date_format, $this->data['updated']['parsed']); } } + else + { + return null; + } } /** - * Parse a header value while within quotes + * Get the localized posting date/time for the item * - * @access private + * Returns the date formatted in the localized language. To display in + * languages other than the server's default, you need to change the locale + * with {@link http://php.net/setlocale setlocale()}. The available + * localizations depend on which ones are installed on your web server. + * + * @since 1.0 + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data) + * @return int|string|null */ - function quote_char() + public function get_local_date($date_format = '%c') { - $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; + if (!$date_format) + { + return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($date = $this->get_date('U')) !== null && $date !== false) + { + return strftime($date_format, $date); + } + else + { + return null; + } } /** - * Parse an escaped character within quotes + * Get the posting date/time for the item (UTC time) * - * @access private + * @see get_date + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} + * @return int|string|null */ - function quote_escaped() + public function get_gmdate($date_format = 'j F Y, g:i a') { - $this->value .= $this->data[$this->position]; - $this->position++; - $this->state = 'quote'; + $date = $this->get_date('U'); + if ($date === null) + { + return null; + } + + return gmdate($date_format, $date); } /** - * Parse the body + * Get the update date/time for the item (UTC time) * - * @access private + * @see get_updated_date + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} + * @return int|string|null */ - function body() + public function get_updated_gmdate($date_format = 'j F Y, g:i a') { - $this->body = substr($this->data, $this->position); - $this->state = 'emit'; - } -} - -/** - * gzdecode - * - * @package SimplePie - */ -class SimplePie_gzdecode -{ - /** - * Compressed data - * - * @access private - * @see gzdecode::$data - */ - var $compressed_data; - - /** - * Size of compressed data - * - * @access private - */ - var $compressed_size; - - /** - * Minimum size of a valid gzip string - * - * @access private - */ - var $min_compressed_size = 18; - - /** - * Current position of pointer - * - * @access private - */ - var $position = 0; - - /** - * Flags (FLG) - * - * @access private - */ - var $flags; - - /** - * Uncompressed data - * - * @access public - * @see gzdecode::$compressed_data - */ - var $data; - - /** - * Modified time - * - * @access public - */ - var $MTIME; - - /** - * Extra Flags - * - * @access public - */ - var $XFL; - - /** - * Operating System - * - * @access public - */ - var $OS; - - /** - * Subfield ID 1 - * - * @access public - * @see gzdecode::$extra_field - * @see gzdecode::$SI2 - */ - var $SI1; - - /** - * Subfield ID 2 - * - * @access public - * @see gzdecode::$extra_field - * @see gzdecode::$SI1 - */ - var $SI2; - - /** - * Extra field content - * - * @access public - * @see gzdecode::$SI1 - * @see gzdecode::$SI2 - */ - var $extra_field; + $date = $this->get_updated_date('U'); + if ($date === null) + { + return null; + } - /** - * Original filename - * - * @access public - */ - var $filename; + return gmdate($date_format, $date); + } /** - * Human readable comment + * Get the permalink for the item * - * @access public - */ - var $comment; - - /** - * Don't allow anything to be set + * Returns the first link available with a relationship of "alternate". + * Identical to {@see get_link()} with key 0 * - * @access public + * @see get_link + * @since 0.8 + * @return string|null Permalink URL */ - function __set($name, $value) + public function get_permalink() { - trigger_error("Cannot write property $name", E_USER_ERROR); + $link = $this->get_link(); + $enclosure = $this->get_enclosure(0); + if ($link !== null) + { + return $link; + } + elseif ($enclosure !== null) + { + return $enclosure->get_link(); + } + else + { + return null; + } } /** - * Set the compressed string and related properties + * Get a single link for the item * - * @access public + * @since Beta 3 + * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 + * @param string $rel The relationship of the link to return + * @return string|null Link URL */ - function SimplePie_gzdecode($data) + public function get_link($key = 0, $rel = 'alternate') { - $this->compressed_data = $data; - $this->compressed_size = strlen($data); + $links = $this->get_links($rel); + if ($links[$key] !== null) + { + return $links[$key]; + } + else + { + return null; + } } /** - * Decode the GZIP stream + * Get all links for the item * - * @access public + * Uses `<atom:link>`, `<link>` or `<guid>` + * + * @since Beta 2 + * @param string $rel The relationship of links to return + * @return array|null Links found for the item (strings) */ - function parse() + public function get_links($rel = 'alternate') { - if ($this->compressed_size >= $this->min_compressed_size) + if (!isset($this->data['links'])) { - // Check ID1, ID2, and CM - if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") + $this->data['links'] = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) { - return false; - } - - // Get the FLG (FLaGs) - $this->flags = ord($this->compressed_data[3]); + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - // FLG bits above (1 << 4) are reserved - if ($this->flags > 0x1F) + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) { - return false; + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } } - - // Advance the pointer after the above - $this->position += 4; - - // MTIME - $mtime = substr($this->compressed_data, $this->position, 4); - // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness - if (current(unpack('S', "\x00\x01")) === 1) + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { - $mtime = strrev($mtime); + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } - $this->MTIME = current(unpack('l', $mtime)); - $this->position += 4; - - // Get the XFL (eXtra FLags) - $this->XFL = ord($this->compressed_data[$this->position++]); - - // Get the OS (Operating System) - $this->OS = ord($this->compressed_data[$this->position++]); - - // Parse the FEXTRA - if ($this->flags & 4) + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) { - // Read subfield IDs - $this->SI1 = $this->compressed_data[$this->position++]; - $this->SI2 = $this->compressed_data[$this->position++]; - - // SI2 set to zero is reserved for future use - if ($this->SI2 === "\x00") - { - return false; - } - - // Get the length of the extra field - $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); - $this->position += 2; - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 4; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Set the extra field to the given data - $this->extra_field = substr($this->compressed_data, $this->position, $len); - $this->position += $len; - } - else + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + { + if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') { - return false; + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } } - // Parse the FNAME - if ($this->flags & 8) + $keys = array_keys($this->data['links']); + foreach ($keys as $key) { - // Get the length of the filename - $len = strcspn($this->compressed_data, "\x00", $this->position); - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 1; - if ($this->compressed_size >= $this->min_compressed_size) + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { - // Set the original filename to the given string - $this->filename = substr($this->compressed_data, $this->position, $len); - $this->position += $len + 1; + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } } - else + elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) { - return false; + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); } - - // Parse the FCOMMENT - if ($this->flags & 16) - { - // Get the length of the comment - $len = strcspn($this->compressed_data, "\x00", $this->position); - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 1; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Set the original comment to the given string - $this->comment = substr($this->compressed_data, $this->position, $len); - $this->position += $len + 1; - } - else - { - return false; - } - } - - // Parse the FHCRC - if ($this->flags & 2) - { - // Check the length of the string is still valid - $this->min_compressed_size += $len + 2; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Read the CRC - $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); - - // Check the CRC matches - if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) - { - $this->position += 2; - } - else - { - return false; - } - } - else - { - return false; - } - } - - // Decompress the actual data - if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) - { - return false; - } - else - { - $this->position = $this->compressed_size - 8; - } - - // Check CRC of data - $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); - $this->position += 4; - /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) - { - return false; - }*/ - - // Check ISIZE of data - $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); - $this->position += 4; - if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) - { - return false; - } - - // Wow, against all odds, we've actually got a valid gzip string - return true; + } + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; } else { - return false; + return null; } } -} -class SimplePie_Cache -{ /** - * Don't call the constructor. Please. + * Get an enclosure from the item * - * @access private + * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. + * + * @since Beta 2 + * @todo Add ability to prefer one type of content over another (in a media group). + * @param int $key The enclosure that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Enclosure|null */ - function SimplePie_Cache() + public function get_enclosure($key = 0, $prefer = null) { - trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR); + $enclosures = $this->get_enclosures(); + if (isset($enclosures[$key])) + { + return $enclosures[$key]; + } + else + { + return null; + } } /** - * Create a new SimplePie_Cache object + * Get all available enclosures (podcasts, etc.) * - * @static - * @access public + * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. + * + * At this point, we're pretty much assuming that all enclosures for an item + * are the same content. Anything else is too complicated to + * properly support. + * + * @since Beta 2 + * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). + * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). + * @return array|null List of SimplePie_Enclosure items */ - function create($location, $filename, $extension) + public function get_enclosures() { - $location_iri = new SimplePie_IRI($location); - switch ($location_iri->get_scheme()) + if (!isset($this->data['enclosures'])) { - case 'mysql': - if (extension_loaded('mysql')) - { - return new SimplePie_Cache_MySQL($location_iri, $filename, $extension); - } - break; - - default: - return new SimplePie_Cache_File($location, $filename, $extension); - } - } -} - -class SimplePie_Cache_File -{ - var $location; - var $filename; - var $extension; - var $name; - - function SimplePie_Cache_File($location, $filename, $extension) - { - $this->location = $location; - $this->filename = $filename; - $this->extension = $extension; - $this->name = "$this->location/$this->filename.$this->extension"; - } + $this->data['enclosures'] = array(); - function save($data) - { - if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) - { - if (is_a($data, 'SimplePie')) - { - $data = $data->data; - } + // Elements + $captions_parent = null; + $categories_parent = null; + $copyrights_parent = null; + $credits_parent = null; + $description_parent = null; + $duration_parent = null; + $hashes_parent = null; + $keywords_parent = null; + $player_parent = null; + $ratings_parent = null; + $restrictions_parent = null; + $thumbnails_parent = null; + $title_parent = null; - $data = serialize($data); + // Let's do the channel and item-level ones first, and just re-use them if we need to. + $parent = $this->get_feed(); - if (function_exists('file_put_contents')) + // CAPTIONS + if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) { - return (bool) file_put_contents($this->name, $data); + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } } - else + elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) { - $fp = fopen($this->name, 'wb'); - if ($fp) + foreach ($captions as $caption) { - fwrite($fp, $data); - fclose($fp); - return true; + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } } - } - return false; - } - - function load() - { - if (file_exists($this->name) && is_readable($this->name)) - { - return unserialize(file_get_contents($this->name)); - } - return false; - } - - function mtime() - { - if (file_exists($this->name)) - { - return filemtime($this->name); - } - return false; - } - - function touch() - { - if (file_exists($this->name)) - { - return touch($this->name); - } - return false; - } - - function unlink() - { - if (file_exists($this->name)) - { - return unlink($this->name); - } - return false; - } -} - -class SimplePie_Cache_DB -{ - function prepare_simplepie_object_for_cache($data) - { - $items = $data->get_items(); - $items_by_id = array(); - - if (!empty($items)) - { - foreach ($items as $item) + if (is_array($captions_parent)) { - $items_by_id[$item->get_id()] = $item; + $captions_parent = array_values(array_unique($captions_parent)); } - if (count($items_by_id) !== count($items)) + // CATEGORIES + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) { - $items_by_id = array(); - foreach ($items as $item) + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) { - $items_by_id[$item->get_id(true)] = $item; + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - } - - if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; - } - else - { - $channel = null; - } - - if ($channel !== null) - { - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) + if (isset($category['attribs']['']['scheme'])) { - unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) + else { - unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); + $scheme = 'http://search.yahoo.com/mrss/category_schema'; } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) + if (isset($category['attribs']['']['label'])) { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) + if (isset($category['attribs']['']['scheme'])) { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } - } - if (isset($data->data['items'])) - { - unset($data->data['items']); - } - if (isset($data->data['ordered_items'])) - { - unset($data->data['ordered_items']); - } - } - return array(serialize($data->data), $items_by_id); - } -} - -class SimplePie_Cache_MySQL extends SimplePie_Cache_DB -{ - var $mysql; - var $options; - var $id; - - function SimplePie_Cache_MySQL($mysql_location, $name, $extension) - { - $host = $mysql_location->get_host(); - if (SimplePie_Misc::stripos($host, 'unix(') === 0 && substr($host, -1) === ')') - { - $server = ':' . substr($host, 5, -1); - } - else - { - $server = $host; - if ($mysql_location->get_port() !== null) - { - $server .= ':' . $mysql_location->get_port(); - } - } - - if (strpos($mysql_location->get_userinfo(), ':') !== false) - { - list($username, $password) = explode(':', $mysql_location->get_userinfo(), 2); - } - else - { - $username = $mysql_location->get_userinfo(); - $password = null; - } - - if ($this->mysql = mysql_connect($server, $username, $password)) - { - $this->id = $name . $extension; - $this->options = SimplePie_Misc::parse_str($mysql_location->get_query()); - if (!isset($this->options['prefix'][0])) - { - $this->options['prefix'][0] = ''; - } - - if (mysql_select_db(ltrim($mysql_location->get_path(), '/')) - && mysql_query('SET NAMES utf8') - && ($query = mysql_unbuffered_query('SHOW TABLES'))) - { - $db = array(); - while ($row = mysql_fetch_row($query)) + else { - $db[] = $row[0]; + $scheme = 'http://search.yahoo.com/mrss/category_schema'; } - - if (!in_array($this->options['prefix'][0] . 'cache_data', $db)) + if (isset($category['attribs']['']['label'])) { - if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))')) - { - $this->mysql = null; - } + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) + { + $term = null; + $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + $label = null; + if (isset($category['attribs']['']['text'])) + { + $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); - if (!in_array($this->options['prefix'][0] . 'items', $db)) + if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) { - if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))')) + foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) { - $this->mysql = null; + if (isset($subcategory['attribs']['']['text'])) + { + $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); } } } - else + if (is_array($categories_parent)) { - $this->mysql = null; + $categories_parent = array_values(array_unique($categories_parent)); } - } - } - - function save($data) - { - if ($this->mysql) - { - $feed_id = "'" . mysql_real_escape_string($this->id) . "'"; - if (is_a($data, 'SimplePie')) + // COPYRIGHT + if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) { - if (SIMPLEPIE_PHP5) + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) { - // This keyword needs to defy coding standards for PHP4 compatibility - $data = clone($data); + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } + $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } - $prepared = $this->prepare_simplepie_object_for_cache($data); - - if ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql)) + // CREDITS + if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) { - if (mysql_num_rows($query)) + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) { - $items = count($prepared[1]); - if ($items) - { - $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = ' . $items . ', `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id; - } - else - { - $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id; - } - - if (!mysql_query($sql, $this->mysql)) - { - return false; - } + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } - elseif (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(' . $feed_id . ', ' . count($prepared[1]) . ', \'' . mysql_real_escape_string($prepared[0]) . '\', ' . time() . ')', $this->mysql)) + if (isset($credit['attribs']['']['scheme'])) { - return false; + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } - - $ids = array_keys($prepared[1]); - if (!empty($ids)) + else { - foreach ($ids as $id) - { - $database_ids[] = mysql_real_escape_string($id); - } - - if ($query = mysql_unbuffered_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'items` WHERE `id` = \'' . implode('\' OR `id` = \'', $database_ids) . '\' AND `feed_id` = ' . $feed_id, $this->mysql)) - { - $existing_ids = array(); - while ($row = mysql_fetch_row($query)) - { - $existing_ids[] = $row[0]; - } - - $new_ids = array_diff($ids, $existing_ids); - - foreach ($new_ids as $new_id) - { - if (!($date = $prepared[1][$new_id]->get_date('U'))) - { - $date = time(); - } - - if (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(' . $feed_id . ', \'' . mysql_real_escape_string($new_id) . '\', \'' . mysql_real_escape_string(serialize($prepared[1][$new_id]->data)) . '\', ' . $date . ')', $this->mysql)) - { - return false; - } - } - return true; - } + $credit_scheme = 'urn:ebu'; } - else + if (isset($credit['data'])) { - return true; + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } + $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } } - elseif ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql)) + elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) { - if (mysql_num_rows($query)) + foreach ($credits as $credit) { - if (mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = 0, `data` = \'' . mysql_real_escape_string(serialize($data)) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id, $this->mysql)) + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) { - return true; + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } - elseif (mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(\'' . mysql_real_escape_string($this->id) . '\', 0, \'' . mysql_real_escape_string(serialize($data)) . '\', ' . time() . ')', $this->mysql)) - { - return true; - } - } - } - return false; - } - - function load() - { - if ($this->mysql && ($query = mysql_query('SELECT `items`, `data` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query))) - { - $data = unserialize($row[1]); - - if (isset($this->options['items'][0])) - { - $items = (int) $this->options['items'][0]; } - else + if (is_array($credits_parent)) { - $items = (int) $row[0]; + $credits_parent = array_values(array_unique($credits_parent)); } - if ($items !== 0) + // DESCRIPTION + if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) { - if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; - } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) + if (isset($description_parent[0]['data'])) { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) + } + elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; - } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]; - } - else - { - $feed = null; + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } + } - if ($feed !== null) + // DURATION + if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) + { + $seconds = null; + $minutes = null; + $hours = null; + if (isset($duration_parent[0]['data'])) { - $sql = 'SELECT `data` FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . '\' ORDER BY `posted` DESC'; - if ($items > 0) + $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + if (sizeof($temp) > 0) { - $sql .= ' LIMIT ' . $items; + $seconds = (int) array_pop($temp); } - - if ($query = mysql_unbuffered_query($sql, $this->mysql)) + if (sizeof($temp) > 0) { - while ($row = mysql_fetch_row($query)) - { - $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row[0]); - } + $minutes = (int) array_pop($temp); + $seconds += $minutes * 60; } - else + if (sizeof($temp) > 0) { - return false; + $hours = (int) array_pop($temp); + $seconds += $hours * 3600; } + unset($temp); + $duration_parent = $seconds; } } - return $data; - } - return false; - } - - function mtime() - { - if ($this->mysql && ($query = mysql_query('SELECT `mtime` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query))) - { - return $row[0]; - } - else - { - return false; - } - } - - function touch() - { - if ($this->mysql && ($query = mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `mtime` = ' . time() . ' WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && mysql_affected_rows($this->mysql)) - { - return true; - } - else - { - return false; - } - } - - function unlink() - { - if ($this->mysql && ($query = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($query2 = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql))) - { - return true; - } - else - { - return false; - } - } -} - -class SimplePie_Misc -{ - function time_hms($seconds) - { - $time = ''; - - $hours = floor($seconds / 3600); - $remainder = $seconds % 3600; - if ($hours > 0) - { - $time .= $hours.':'; - } - - $minutes = floor($remainder / 60); - $seconds = $remainder % 60; - if ($minutes < 10 && $hours > 0) - { - $minutes = '0' . $minutes; - } - if ($seconds < 10) - { - $seconds = '0' . $seconds; - } - - $time .= $minutes.':'; - $time .= $seconds; - - return $time; - } - function absolutize_url($relative, $base) - { - $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); - return $iri->get_iri(); - } - - function remove_dot_segments($input) - { - $output = ''; - while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') - { - // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, - if (strpos($input, '../') === 0) - { - $input = substr($input, 3); - } - elseif (strpos($input, './') === 0) + // HASHES + if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) { - $input = substr($input, 2); + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } } - // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, - elseif (strpos($input, '/./') === 0) + elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) { - $input = substr_replace($input, '/', 0, 3); + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } } - elseif ($input === '/.') + if (is_array($hashes_parent)) { - $input = '/'; + $hashes_parent = array_values(array_unique($hashes_parent)); } - // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, - elseif (strpos($input, '/../') === 0) + + // KEYWORDS + if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) { - $input = substr_replace($input, '/', 0, 4); - $output = substr_replace($output, '', strrpos($output, '/')); + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); } - elseif ($input === '/..') + elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) { - $input = '/'; - $output = substr_replace($output, '', strrpos($output, '/')); + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); } - // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, - elseif ($input === '.' || $input === '..') + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) { - $input = ''; + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); } - // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer - elseif (($pos = strpos($input, '/', 1)) !== false) + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) { - $output .= substr($input, 0, $pos); - $input = substr_replace($input, '', 0, $pos); + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); } - else + if (is_array($keywords_parent)) { - $output .= $input; - $input = ''; + $keywords_parent = array_values(array_unique($keywords_parent)); } - } - return $output . $input; - } - function get_element($realname, $string) - { - $return = array(); - $name = preg_quote($realname, '/'); - if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) - { - for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) + // PLAYER + if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) { - $return[$i]['tag'] = $realname; - $return[$i]['full'] = $matches[$i][0][0]; - $return[$i]['offset'] = $matches[$i][0][1]; - if (strlen($matches[$i][3][0]) <= 2) - { - $return[$i]['self_closing'] = true; - } - else + if (isset($player_parent[0]['attribs']['']['url'])) { - $return[$i]['self_closing'] = false; - $return[$i]['content'] = $matches[$i][4][0]; + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } - $return[$i]['attribs'] = array(); - if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) + } + elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) { - for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) - { - if (count($attribs[$j]) === 2) - { - $attribs[$j][2] = $attribs[$j][1]; - } - $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); - } + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } } - } - return $return; - } - function element_implode($element) - { - $full = "<$element[tag]"; - foreach ($element['attribs'] as $key => $value) - { - $key = strtolower($key); - $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; - } - if ($element['self_closing']) - { - $full .= ' />'; - } - else - { - $full .= ">$element[content]</$element[tag]>"; - } - return $full; - } - - function error($message, $level, $file, $line) - { - if ((ini_get('error_reporting') & $level) > 0) - { - switch ($level) + // RATINGS + if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) { - case E_USER_ERROR: - $note = 'PHP Error'; - break; - case E_USER_WARNING: - $note = 'PHP Warning'; - break; - case E_USER_NOTICE: - $note = 'PHP Notice'; - break; - default: - $note = 'Unknown Error'; - break; + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } } - - $log_error = true; - if (!function_exists('error_log')) + elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) { - $log_error = false; + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } } - - $log_file = @ini_get('error_log'); - if (!empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file)) + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) { - $log_error = false; + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } } - - if ($log_error) + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) { - @error_log("$note: $message in $file on line $line", 0); + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + } + if (is_array($ratings_parent)) + { + $ratings_parent = array_values(array_unique($ratings_parent)); } - } - - return $message; - } - - /** - * If a file has been cached, retrieve and display it. - * - * This is most useful for caching images (get_favicon(), etc.), - * however it works for all cached files. This WILL NOT display ANY - * file/image/page/whatever, but rather only display what has already - * been cached by SimplePie. - * - * @access public - * @see SimplePie::get_favicon() - * @param str $identifier_url URL that is used to identify the content. - * This may or may not be the actual URL of the live content. - * @param str $cache_location Location of SimplePie's cache. Defaults - * to './cache'. - * @param str $cache_extension The file extension that the file was - * cached with. Defaults to 'spc'. - * @param str $cache_class Name of the cache-handling class being used - * in SimplePie. Defaults to 'SimplePie_Cache', and should be left - * as-is unless you've overloaded the class. - * @param str $cache_name_function Obsolete. Exists for backwards - * compatibility reasons only. - */ - function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5') - { - $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension); - if ($file = $cache->load()) - { - if (isset($file['headers']['content-type'])) + // RESTRICTIONS + if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + if (is_array($restrictions_parent)) { - header('Content-type:' . $file['headers']['content-type']); + $restrictions_parent = array_values(array_unique($restrictions_parent)); } else { - header('Content-type: application/octet-stream'); + $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default')); } - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - echo $file['body']; - exit; - } - - die('Cached file for ' . $identifier_url . ' cannot be found.'); - } - function fix_protocol($url, $http = 1) - { - $url = SimplePie_Misc::normalize_url($url); - $parsed = SimplePie_Misc::parse_url($url); - if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); - } + // THUMBNAILS + if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } - if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); - } + // TITLES + if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } - if ($http === 2 && $parsed['scheme'] !== '') - { - return "feed:$url"; - } - elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') - { - return substr_replace($url, 'podcast', 0, 4); - } - elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') - { - return substr_replace($url, 'itpc', 0, 4); - } - else - { - return $url; - } - } + // Clear the memory + unset($parent); - function parse_url($url) - { - $iri = new SimplePie_IRI($url); - return array( - 'scheme' => (string) $iri->get_scheme(), - 'authority' => (string) $iri->get_authority(), - 'path' => (string) $iri->get_path(), - 'query' => (string) $iri->get_query(), - 'fragment' => (string) $iri->get_fragment() - ); - } - - function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') - { - $iri = new SimplePie_IRI(''); - $iri->set_scheme($scheme); - $iri->set_authority($authority); - $iri->set_path($path); - $iri->set_query($query); - $iri->set_fragment($fragment); - return $iri->get_iri(); - } - - function normalize_url($url) - { - $iri = new SimplePie_IRI($url); - return $iri->get_iri(); - } - - function percent_encoding_normalization($match) - { - $integer = hexdec($match[1]); - if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) - { - return chr($integer); - } - else - { - return strtoupper($match[0]); - } - } - - /** - * Remove bad UTF-8 bytes - * - * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C - * FAQ: Multilingual Forms (modified to include full ASCII range) - * - * @author Geoffrey Sneddon - * @see http://www.w3.org/International/questions/qa-forms-utf-8 - * @param string $str String to remove bad UTF-8 bytes from - * @return string UTF-8 string - */ - function utf8_bad_replace($str) - { - if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str))) - { - return $return; - } - elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8'))) - { - return $return; - } - elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches)) - { - return implode("\xEF\xBF\xBD", $matches[0]); - } - elseif ($str !== '') - { - return "\xEF\xBF\xBD"; - } - else - { - return ''; - } - } - - /** - * Converts a Windows-1252 encoded string to a UTF-8 encoded string - * - * @static - * @access public - * @param string $string Windows-1252 encoded string - * @return string UTF-8 encoded string - */ - function windows_1252_to_utf8($string) - { - static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); - - return strtr($string, $convert_table); - } + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - function change_encoding($data, $input, $output) - { - $input = SimplePie_Misc::encoding($input); - $output = SimplePie_Misc::encoding($output); + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; - // We fail to fail on non US-ASCII bytes - if ($input === 'US-ASCII') - { - static $non_ascii_octects = ''; - if (!$non_ascii_octects) + // If we have media:group tags, loop through them. + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) { - for ($i = 0x80; $i <= 0xFF; $i++) + if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) { - $non_ascii_octects .= chr($i); - } - } - $data = substr($data, 0, strcspn($data, $non_ascii_octects)); - } - - // This is first, as behaviour of this is completely predictable - if ($input === 'windows-1252' && $output === 'UTF-8') - { - return SimplePie_Misc::windows_1252_to_utf8($data); - } - // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported). - elseif (function_exists('mb_convert_encoding') && @mb_convert_encoding("\x80", 'UTF-16BE', $input) !== "\x00\x80" && in_array($input, mb_list_encodings()) && ($return = @mb_convert_encoding($data, $output, $input))) - { - return $return; - } - // This is last, as behaviour of this varies with OS userland and PHP version - elseif (function_exists('iconv') && ($return = @iconv($input, $output, $data))) - { - return $return; - } - // If we can't do anything, just fail - else - { - return false; - } - } - - /** - * Normalize an encoding name - * - * This is automatically generated by create.php - * - * To generate it, run `php create.php` on the command line, and copy the - * output to replace this function. - * - * @param string $charset Character set to standardise - * @return string Standardised name - */ - function encoding($charset) - { - // Normalization from UTS #22 - switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) - { - case 'adobestandardencoding': - case 'csadobestandardencoding': - return 'Adobe-Standard-Encoding'; - - case 'adobesymbolencoding': - case 'cshppsmath': - return 'Adobe-Symbol-Encoding'; - - case 'ami1251': - case 'amiga1251': - return 'Amiga-1251'; + // If we have media:content tags, loop through them. + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - case 'ansix31101983': - case 'csat5001983': - case 'csiso99naplps': - case 'isoir99': - case 'naplps': - return 'ANSI_X3.110-1983'; + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; - case 'arabic7': - case 'asmo449': - case 'csiso89asmo449': - case 'iso9036': - case 'isoir89': - return 'ASMO_449'; + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - case 'big5': - case 'csbig5': - return 'Big5'; + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - case 'big5hkscs': - return 'Big5-HKSCS'; + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } - case 'bocu1': - case 'csbocu1': - return 'BOCU-1'; + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(array_unique($categories_parent)); + } - case 'brf': - case 'csbrf': - return 'BRF'; + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + else + { + $copyrights = $copyrights_parent; + } - case 'bs4730': - case 'csiso4unitedkingdom': - case 'gb': - case 'iso646gb': - case 'isoir4': - case 'uk': - return 'BS_4730'; + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } - case 'bsviewdata': - case 'csiso47bsviewdata': - case 'isoir47': - return 'BS_viewdata'; + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } - case 'cesu8': - case 'cscesu8': - return 'CESU-8'; - - case 'ca': - case 'csa71': - case 'csaz243419851': - case 'csiso121canadian1': - case 'iso646ca': - case 'isoir121': - return 'CSA_Z243.4-1985-1'; - - case 'csa72': - case 'csaz243419852': - case 'csiso122canadian2': - case 'iso646ca2': - case 'isoir122': - return 'CSA_Z243.4-1985-2'; - - case 'csaz24341985gr': - case 'csiso123csaz24341985gr': - case 'isoir123': - return 'CSA_Z243.4-1985-gr'; - - case 'csiso139csn369103': - case 'csn369103': - case 'isoir139': - return 'CSN_369103'; - - case 'csdecmcs': - case 'dec': - case 'decmcs': - return 'DEC-MCS'; + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } - case 'csiso21german': - case 'de': - case 'din66003': - case 'iso646de': - case 'isoir21': - return 'DIN_66003'; + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } - case 'csdkus': - case 'dkus': - return 'dk-us'; + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } - case 'csiso646danish': - case 'dk': - case 'ds2089': - case 'iso646dk': - return 'DS_2089'; + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } - case 'csibmebcdicatde': - case 'ebcdicatde': - return 'EBCDIC-AT-DE'; + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } - case 'csebcdicatdea': - case 'ebcdicatdea': - return 'EBCDIC-AT-DE-A'; + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } - case 'csebcdiccafr': - case 'ebcdiccafr': - return 'EBCDIC-CA-FR'; + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } - case 'csebcdicdkno': - case 'ebcdicdkno': - return 'EBCDIC-DK-NO'; + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); + } + } + } + } - case 'csebcdicdknoa': - case 'ebcdicdknoa': - return 'EBCDIC-DK-NO-A'; + // If we have standalone media:content tags, loop through them. + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) + { + foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - case 'csebcdices': - case 'ebcdices': - return 'EBCDIC-ES'; + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; - case 'csebcdicesa': - case 'ebcdicesa': - return 'EBCDIC-ES-A'; + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['url'])) + { + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - case 'csebcdicess': - case 'ebcdicess': - return 'EBCDIC-ES-S'; + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } - case 'csebcdicfise': - case 'ebcdicfise': - return 'EBCDIC-FI-SE'; + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(array_unique($categories_parent)); + } + else + { + $categories = null; + } - case 'csebcdicfisea': - case 'ebcdicfisea': - return 'EBCDIC-FI-SE-A'; + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + else + { + $copyrights = $copyrights_parent; + } - case 'csebcdicfr': - case 'ebcdicfr': - return 'EBCDIC-FR'; - - case 'csebcdicit': - case 'ebcdicit': - return 'EBCDIC-IT'; + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } - case 'csebcdicpt': - case 'ebcdicpt': - return 'EBCDIC-PT'; + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } - case 'csebcdicuk': - case 'ebcdicuk': - return 'EBCDIC-UK'; + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } - case 'csebcdicus': - case 'ebcdicus': - return 'EBCDIC-US'; + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } - case 'csiso111ecmacyrillic': - case 'ecmacyrillic': - case 'isoir111': - case 'koi8e': - return 'ECMA-cyrillic'; + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } - case 'csiso17spanish': - case 'es': - case 'iso646es': - case 'isoir17': - return 'ES'; + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } - case 'csiso85spanish2': - case 'es2': - case 'iso646es2': - case 'isoir85': - return 'ES2'; + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } - case 'cseucpkdfmtjapanese': - case 'eucjp': - case 'extendedunixcodepackedformatforjapanese': - return 'EUC-JP'; + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } - case 'cseucfixwidjapanese': - case 'extendedunixcodefixedwidthforjapanese': - return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } - case 'gb18030': - return 'GB18030'; + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); + } + } + } - case 'chinese': - case 'cp936': - case 'csgb2312': - case 'csiso58gb231280': - case 'gb2312': - case 'gb231280': - case 'gbk': - case 'isoir58': - case 'ms936': - case 'windows936': - return 'GBK'; + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - case 'cn': - case 'csiso57gb1988': - case 'gb198880': - case 'iso646cn': - case 'isoir57': - return 'GB_1988-80'; + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } - case 'csiso153gost1976874': - case 'gost1976874': - case 'isoir153': - case 'stsev35888': - return 'GOST_19768-74'; + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } - case 'csiso150': - case 'csiso150greekccitt': - case 'greekccitt': - case 'isoir150': - return 'greek-ccitt'; + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - case 'csiso88greek7': - case 'greek7': - case 'isoir88': - return 'greek7'; + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } - case 'csiso18greek7old': - case 'greek7old': - case 'isoir18': - return 'greek7-old'; + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } - case 'cshpdesktop': - case 'hpdesktop': - return 'HP-DeskTop'; + if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure')) + { + if (isset($enclosure[0]['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; - case 'cshplegal': - case 'hplegal': - return 'HP-Legal'; + $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); + if (isset($enclosure[0]['attribs']['']['type'])) + { + $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($enclosure[0]['attribs']['']['length'])) + { + $length = ceil($enclosure[0]['attribs']['']['length']); + } - case 'cshpmath8': - case 'hpmath8': - return 'HP-Math8'; + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } - case 'cshppifont': - case 'hppifont': - return 'HP-Pi-font'; + if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) + { + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } - case 'cshproman8': - case 'hproman8': - case 'r8': - case 'roman8': - return 'hp-roman8'; + $this->data['enclosures'] = array_values(array_unique($this->data['enclosures'])); + } + if (!empty($this->data['enclosures'])) + { + return $this->data['enclosures']; + } + else + { + return null; + } + } - case 'hzgb2312': - return 'HZ-GB-2312'; + /** + * Get the latitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:lat>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_latitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } - case 'csibmsymbols': - case 'ibmsymbols': - return 'IBM-Symbols'; + /** + * Get the longitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_longitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } - case 'csibmthai': - case 'ibmthai': - return 'IBM-Thai'; + /** + * Get the `<atom:source>` for the item + * + * @since 1.1 + * @return SimplePie_Source|null + */ + public function get_source() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) + { + return $this->registry->create('Source', array($this, $return[0])); + } + else + { + return null; + } + } +} - case 'cp37': - case 'csibm37': - case 'ebcdiccpca': - case 'ebcdiccpnl': - case 'ebcdiccpus': - case 'ebcdiccpwt': - case 'ibm37': - return 'IBM037'; +/** + * Used for feed auto-discovery + * + * + * This class can be overloaded with {@see SimplePie::set_locator_class()} + * + * @package SimplePie + */ +class SimplePie_Locator +{ + var $useragent; + var $timeout; + var $file; + var $local = array(); + var $elsewhere = array(); + var $cached_entities = array(); + var $http_base; + var $base; + var $base_location = 0; + var $checked_feeds = 0; + var $max_checked_feeds = 10; + protected $registry; - case 'cp38': - case 'csibm38': - case 'ebcdicint': - case 'ibm38': - return 'IBM038'; + public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10) + { + $this->file = $file; + $this->useragent = $useragent; + $this->timeout = $timeout; + $this->max_checked_feeds = $max_checked_feeds; - case 'cp273': - case 'csibm273': - case 'ibm273': - return 'IBM273'; + if (class_exists('DOMDocument')) + { + $this->dom = new DOMDocument(); - case 'cp274': - case 'csibm274': - case 'ebcdicbe': - case 'ibm274': - return 'IBM274'; + set_error_handler(array('SimplePie_Misc', 'silence_errors')); + $this->dom->loadHTML($this->file->body); + restore_error_handler(); + } + else + { + $this->dom = null; + } + } - case 'cp275': - case 'csibm275': - case 'ebcdicbr': - case 'ibm275': - return 'IBM275'; + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } - case 'csibm277': - case 'ebcdiccpdk': - case 'ebcdiccpno': - case 'ibm277': - return 'IBM277'; + public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working) + { + if ($this->is_feed($this->file)) + { + return $this->file; + } - case 'cp278': - case 'csibm278': - case 'ebcdiccpfi': - case 'ebcdiccpse': - case 'ibm278': - return 'IBM278'; + if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file)); + if ($sniffer->get_type() !== 'text/html') + { + return null; + } + } - case 'cp280': - case 'csibm280': - case 'ebcdiccpit': - case 'ibm280': - return 'IBM280'; + if ($type & ~SIMPLEPIE_LOCATOR_NONE) + { + $this->get_base(); + } - case 'cp281': - case 'csibm281': - case 'ebcdicjpe': - case 'ibm281': - return 'IBM281'; + if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) + { + return $working[0]; + } - case 'cp284': - case 'csibm284': - case 'ebcdiccpes': - case 'ibm284': - return 'IBM284'; + if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) + { + if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) + { + return $working; + } - case 'cp285': - case 'csibm285': - case 'ebcdiccpgb': - case 'ibm285': - return 'IBM285'; + if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) + { + return $working; + } - case 'cp290': - case 'csibm290': - case 'ebcdicjpkana': - case 'ibm290': - return 'IBM290'; + if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) + { + return $working; + } - case 'cp297': - case 'csibm297': - case 'ebcdiccpfr': - case 'ibm297': - return 'IBM297'; + if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) + { + return $working; + } + } + return null; + } - case 'cp420': - case 'csibm420': - case 'ebcdiccpar1': - case 'ibm420': - return 'IBM420'; + public function is_feed($file) + { + if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = $this->registry->create('Content_Type_Sniffer', array($file)); + $sniffed = $sniffer->get_type(); + if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) + { + return true; + } + else + { + return false; + } + } + elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) + { + return true; + } + else + { + return false; + } + } - case 'cp423': - case 'csibm423': - case 'ebcdiccpgr': - case 'ibm423': - return 'IBM423'; + public function get_base() + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } + $this->http_base = $this->file->url; + $this->base = $this->http_base; + $elements = $this->dom->getElementsByTagName('base'); + foreach ($elements as $element) + { + if ($element->hasAttribute('href')) + { + $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base)); + if ($base === false) + { + continue; + } + $this->base = $base; + $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0; + break; + } + } + } - case 'cp424': - case 'csibm424': - case 'ebcdiccphe': - case 'ibm424': - return 'IBM424'; + public function autodiscovery() + { + $done = array(); + $feeds = array(); + $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds)); + $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds)); + $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds)); - case '437': - case 'cp437': - case 'cspc8codepage437': - case 'ibm437': - return 'IBM437'; + if (!empty($feeds)) + { + return array_values($feeds); + } + else + { + return null; + } + } - case 'cp500': - case 'csibm500': - case 'ebcdiccpbe': - case 'ebcdiccpch': - case 'ibm500': - return 'IBM500'; + protected function search_elements_by_tag($name, &$done, $feeds) + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } - case 'cp775': - case 'cspc775baltic': - case 'ibm775': - return 'IBM775'; + $links = $this->dom->getElementsByTagName($name); + foreach ($links as $link) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if ($link->hasAttribute('href') && $link->hasAttribute('rel')) + { + $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel'))))); + $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1; - case '850': - case 'cp850': - case 'cspc850multilingual': - case 'ibm850': - return 'IBM850'; + if ($this->base_location < $line) + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); + } + else + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); + } + if ($href === false) + { + continue; + } - case '851': - case 'cp851': - case 'csibm851': - case 'ibm851': - return 'IBM851'; + if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href])) + { + $this->checked_feeds++; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + $feeds[$href] = $feed; + } + } + $done[] = $href; + } + } - case '852': - case 'cp852': - case 'cspcp852': - case 'ibm852': - return 'IBM852'; + return $feeds; + } - case '855': - case 'cp855': - case 'csibm855': - case 'ibm855': - return 'IBM855'; + public function get_links() + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } - case '857': - case 'cp857': - case 'csibm857': - case 'ibm857': - return 'IBM857'; + $links = $this->dom->getElementsByTagName('a'); + foreach ($links as $link) + { + if ($link->hasAttribute('href')) + { + $href = trim($link->getAttribute('href')); + $parsed = $this->registry->call('Misc', 'parse_url', array($href)); + if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) + { + if ($this->base_location < $link->getLineNo()) + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); + } + else + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); + } + if ($href === false) + { + continue; + } - case 'ccsid858': - case 'cp858': - case 'ibm858': - case 'pcmultilingual850euro': - return 'IBM00858'; + $current = $this->registry->call('Misc', 'parse_url', array($this->file->url)); - case '860': - case 'cp860': - case 'csibm860': - case 'ibm860': - return 'IBM860'; + if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) + { + $this->local[] = $href; + } + else + { + $this->elsewhere[] = $href; + } + } + } + } + $this->local = array_unique($this->local); + $this->elsewhere = array_unique($this->elsewhere); + if (!empty($this->local) || !empty($this->elsewhere)) + { + return true; + } + return null; + } - case '861': - case 'cp861': - case 'cpis': - case 'csibm861': - case 'ibm861': - return 'IBM861'; - - case '862': - case 'cp862': - case 'cspc862latinhebrew': - case 'ibm862': - return 'IBM862'; + public function extension(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) + { + $this->checked_feeds++; - case '863': - case 'cp863': - case 'csibm863': - case 'ibm863': - return 'IBM863'; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } - case 'cp864': - case 'csibm864': - case 'ibm864': - return 'IBM864'; + public function body(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if (preg_match('/(rss|rdf|atom|xml)/i', $value)) + { + $this->checked_feeds++; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } +} - case '865': - case 'cp865': - case 'csibm865': - case 'ibm865': - return 'IBM865'; +/** + * Miscellanous utilities + * + * @package SimplePie + */ +class SimplePie_Misc +{ + public static function time_hms($seconds) + { + $time = ''; - case '866': - case 'cp866': - case 'csibm866': - case 'ibm866': - return 'IBM866'; + $hours = floor($seconds / 3600); + $remainder = $seconds % 3600; + if ($hours > 0) + { + $time .= $hours.':'; + } - case 'cp868': - case 'cpar': - case 'csibm868': - case 'ibm868': - return 'IBM868'; + $minutes = floor($remainder / 60); + $seconds = $remainder % 60; + if ($minutes < 10 && $hours > 0) + { + $minutes = '0' . $minutes; + } + if ($seconds < 10) + { + $seconds = '0' . $seconds; + } - case '869': - case 'cp869': - case 'cpgr': - case 'csibm869': - case 'ibm869': - return 'IBM869'; + $time .= $minutes.':'; + $time .= $seconds; - case 'cp870': - case 'csibm870': - case 'ebcdiccproece': - case 'ebcdiccpyu': - case 'ibm870': - return 'IBM870'; + return $time; + } - case 'cp871': - case 'csibm871': - case 'ebcdiccpis': - case 'ibm871': - return 'IBM871'; + public static function absolutize_url($relative, $base) + { + $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); + if ($iri === false) + { + return false; + } + return $iri->get_uri(); + } - case 'cp880': - case 'csibm880': - case 'ebcdiccyrillic': - case 'ibm880': - return 'IBM880'; + /** + * Get a HTML/XML element from a HTML string + * + * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!) + * @param string $realname Element name (including namespace prefix if applicable) + * @param string $string HTML document + * @return array + */ + public static function get_element($realname, $string) + { + $return = array(); + $name = preg_quote($realname, '/'); + if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) + { + for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) + { + $return[$i]['tag'] = $realname; + $return[$i]['full'] = $matches[$i][0][0]; + $return[$i]['offset'] = $matches[$i][0][1]; + if (strlen($matches[$i][3][0]) <= 2) + { + $return[$i]['self_closing'] = true; + } + else + { + $return[$i]['self_closing'] = false; + $return[$i]['content'] = $matches[$i][4][0]; + } + $return[$i]['attribs'] = array(); + if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) + { + for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) + { + if (count($attribs[$j]) === 2) + { + $attribs[$j][2] = $attribs[$j][1]; + } + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); + } + } + } + } + return $return; + } - case 'cp891': - case 'csibm891': - case 'ibm891': - return 'IBM891'; + public static function element_implode($element) + { + $full = "<$element[tag]"; + foreach ($element['attribs'] as $key => $value) + { + $key = strtolower($key); + $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; + } + if ($element['self_closing']) + { + $full .= ' />'; + } + else + { + $full .= ">$element[content]</$element[tag]>"; + } + return $full; + } - case 'cp903': - case 'csibm903': - case 'ibm903': - return 'IBM903'; + public static function error($message, $level, $file, $line) + { + if ((ini_get('error_reporting') & $level) > 0) + { + switch ($level) + { + case E_USER_ERROR: + $note = 'PHP Error'; + break; + case E_USER_WARNING: + $note = 'PHP Warning'; + break; + case E_USER_NOTICE: + $note = 'PHP Notice'; + break; + default: + $note = 'Unknown Error'; + break; + } - case '904': - case 'cp904': - case 'csibbm904': - case 'ibm904': - return 'IBM904'; + $log_error = true; + if (!function_exists('error_log')) + { + $log_error = false; + } - case 'cp905': - case 'csibm905': - case 'ebcdiccptr': - case 'ibm905': - return 'IBM905'; + $log_file = @ini_get('error_log'); + if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file)) + { + $log_error = false; + } - case 'cp918': - case 'csibm918': - case 'ebcdiccpar2': - case 'ibm918': - return 'IBM918'; + if ($log_error) + { + @error_log("$note: $message in $file on line $line", 0); + } + } - case 'ccsid924': - case 'cp924': - case 'ebcdiclatin9euro': - case 'ibm924': - return 'IBM00924'; + return $message; + } - case 'cp1026': - case 'csibm1026': - case 'ibm1026': - return 'IBM1026'; + public static function fix_protocol($url, $http = 1) + { + $url = SimplePie_Misc::normalize_url($url); + $parsed = SimplePie_Misc::parse_url($url); + if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); + } - case 'ibm1047': - return 'IBM1047'; + if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); + } - case 'ccsid1140': - case 'cp1140': - case 'ebcdicus37euro': - case 'ibm1140': - return 'IBM01140'; + if ($http === 2 && $parsed['scheme'] !== '') + { + return "feed:$url"; + } + elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') + { + return substr_replace($url, 'podcast', 0, 4); + } + elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') + { + return substr_replace($url, 'itpc', 0, 4); + } + else + { + return $url; + } + } - case 'ccsid1141': - case 'cp1141': - case 'ebcdicde273euro': - case 'ibm1141': - return 'IBM01141'; + public static function parse_url($url) + { + $iri = new SimplePie_IRI($url); + return array( + 'scheme' => (string) $iri->scheme, + 'authority' => (string) $iri->authority, + 'path' => (string) $iri->path, + 'query' => (string) $iri->query, + 'fragment' => (string) $iri->fragment + ); + } - case 'ccsid1142': - case 'cp1142': - case 'ebcdicdk277euro': - case 'ebcdicno277euro': - case 'ibm1142': - return 'IBM01142'; + public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') + { + $iri = new SimplePie_IRI(''); + $iri->scheme = $scheme; + $iri->authority = $authority; + $iri->path = $path; + $iri->query = $query; + $iri->fragment = $fragment; + return $iri->get_uri(); + } - case 'ccsid1143': - case 'cp1143': - case 'ebcdicfi278euro': - case 'ebcdicse278euro': - case 'ibm1143': - return 'IBM01143'; + public static function normalize_url($url) + { + $iri = new SimplePie_IRI($url); + return $iri->get_uri(); + } - case 'ccsid1144': - case 'cp1144': - case 'ebcdicit280euro': - case 'ibm1144': - return 'IBM01144'; + public static function percent_encoding_normalization($match) + { + $integer = hexdec($match[1]); + if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) + { + return chr($integer); + } + else + { + return strtoupper($match[0]); + } + } - case 'ccsid1145': - case 'cp1145': - case 'ebcdices284euro': - case 'ibm1145': - return 'IBM01145'; + /** + * Converts a Windows-1252 encoded string to a UTF-8 encoded string + * + * @static + * @param string $string Windows-1252 encoded string + * @return string UTF-8 encoded string + */ + public static function windows_1252_to_utf8($string) + { + static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); - case 'ccsid1146': - case 'cp1146': - case 'ebcdicgb285euro': - case 'ibm1146': - return 'IBM01146'; + return strtr($string, $convert_table); + } - case 'ccsid1147': - case 'cp1147': - case 'ebcdicfr297euro': - case 'ibm1147': - return 'IBM01147'; + /** + * Change a string from one encoding to another + * + * @param string $data Raw data in $input encoding + * @param string $input Encoding of $data + * @param string $output Encoding you want + * @return string|boolean False if we can't convert it + */ + public static function change_encoding($data, $input, $output) + { + $input = SimplePie_Misc::encoding($input); + $output = SimplePie_Misc::encoding($output); - case 'ccsid1148': - case 'cp1148': - case 'ebcdicinternational500euro': - case 'ibm1148': - return 'IBM01148'; + // We fail to fail on non US-ASCII bytes + if ($input === 'US-ASCII') + { + static $non_ascii_octects = ''; + if (!$non_ascii_octects) + { + for ($i = 0x80; $i <= 0xFF; $i++) + { + $non_ascii_octects .= chr($i); + } + } + $data = substr($data, 0, strcspn($data, $non_ascii_octects)); + } - case 'ccsid1149': - case 'cp1149': - case 'ebcdicis871euro': - case 'ibm1149': - return 'IBM01149'; + // This is first, as behaviour of this is completely predictable + if ($input === 'windows-1252' && $output === 'UTF-8') + { + return SimplePie_Misc::windows_1252_to_utf8($data); + } + // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported). + elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output))) + { + return $return; + } + // This is last, as behaviour of this varies with OS userland and PHP version + elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output))) + { + return $return; + } + // If we can't do anything, just fail + else + { + return false; + } + } - case 'csiso143iecp271': - case 'iecp271': - case 'isoir143': - return 'IEC_P27-1'; + protected static function change_encoding_mbstring($data, $input, $output) + { + if ($input === 'windows-949') + { + $input = 'EUC-KR'; + } + if ($output === 'windows-949') + { + $output = 'EUC-KR'; + } + if ($input === 'Windows-31J') + { + $input = 'SJIS'; + } + if ($output === 'Windows-31J') + { + $output = 'SJIS'; + } - case 'csiso49inis': - case 'inis': - case 'isoir49': - return 'INIS'; + // Check that the encoding is supported + if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80") + { + return false; + } + if (!in_array($input, mb_list_encodings())) + { + return false; + } - case 'csiso50inis8': - case 'inis8': - case 'isoir50': - return 'INIS-8'; + // Let's do some conversion + if ($return = @mb_convert_encoding($data, $output, $input)) + { + return $return; + } - case 'csiso51iniscyrillic': - case 'iniscyrillic': - case 'isoir51': - return 'INIS-cyrillic'; + return false; + } - case 'csinvariant': - case 'invariant': - return 'INVARIANT'; + protected static function change_encoding_iconv($data, $input, $output) + { + return @iconv($input, $output, $data); + } - case 'iso2022cn': - return 'ISO-2022-CN'; + /** + * Normalize an encoding name + * + * This is automatically generated by create.php + * + * To generate it, run `php create.php` on the command line, and copy the + * output to replace this function. + * + * @param string $charset Character set to standardise + * @return string Standardised name + */ + public static function encoding($charset) + { + // Normalization from UTS #22 + switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) + { + case 'adobestandardencoding': + case 'csadobestandardencoding': + return 'Adobe-Standard-Encoding'; - case 'iso2022cnext': - return 'ISO-2022-CN-EXT'; + case 'adobesymbolencoding': + case 'cshppsmath': + return 'Adobe-Symbol-Encoding'; - case 'csiso2022jp': - case 'iso2022jp': - return 'ISO-2022-JP'; + case 'ami1251': + case 'amiga1251': + return 'Amiga-1251'; - case 'csiso2022jp2': - case 'iso2022jp2': - return 'ISO-2022-JP-2'; + case 'ansix31101983': + case 'csat5001983': + case 'csiso99naplps': + case 'isoir99': + case 'naplps': + return 'ANSI_X3.110-1983'; - case 'csiso2022kr': - case 'iso2022kr': - return 'ISO-2022-KR'; + case 'arabic7': + case 'asmo449': + case 'csiso89asmo449': + case 'iso9036': + case 'isoir89': + return 'ASMO_449'; - case 'cswindows30latin1': - case 'iso88591windows30latin1': - return 'ISO-8859-1-Windows-3.0-Latin-1'; + case 'big5': + case 'csbig5': + return 'Big5'; - case 'cswindows31latin1': - case 'iso88591windows31latin1': - return 'ISO-8859-1-Windows-3.1-Latin-1'; + case 'big5hkscs': + return 'Big5-HKSCS'; - case 'csisolatin2': - case 'iso88592': - case 'iso885921987': - case 'isoir101': - case 'l2': - case 'latin2': - return 'ISO-8859-2'; - - case 'cswindows31latin2': - case 'iso88592windowslatin2': - return 'ISO-8859-2-Windows-Latin-2'; + case 'bocu1': + case 'csbocu1': + return 'BOCU-1'; - case 'csisolatin3': - case 'iso88593': - case 'iso885931988': - case 'isoir109': - case 'l3': - case 'latin3': - return 'ISO-8859-3'; + case 'brf': + case 'csbrf': + return 'BRF'; - case 'csisolatin4': - case 'iso88594': - case 'iso885941988': - case 'isoir110': - case 'l4': - case 'latin4': - return 'ISO-8859-4'; + case 'bs4730': + case 'csiso4unitedkingdom': + case 'gb': + case 'iso646gb': + case 'isoir4': + case 'uk': + return 'BS_4730'; - case 'csisolatincyrillic': - case 'cyrillic': - case 'iso88595': - case 'iso885951988': - case 'isoir144': - return 'ISO-8859-5'; + case 'bsviewdata': + case 'csiso47bsviewdata': + case 'isoir47': + return 'BS_viewdata'; - case 'arabic': - case 'asmo708': - case 'csisolatinarabic': - case 'ecma114': - case 'iso88596': - case 'iso885961987': - case 'isoir127': - return 'ISO-8859-6'; + case 'cesu8': + case 'cscesu8': + return 'CESU-8'; - case 'csiso88596e': - case 'iso88596e': - return 'ISO-8859-6-E'; + case 'ca': + case 'csa71': + case 'csaz243419851': + case 'csiso121canadian1': + case 'iso646ca': + case 'isoir121': + return 'CSA_Z243.4-1985-1'; - case 'csiso88596i': - case 'iso88596i': - return 'ISO-8859-6-I'; + case 'csa72': + case 'csaz243419852': + case 'csiso122canadian2': + case 'iso646ca2': + case 'isoir122': + return 'CSA_Z243.4-1985-2'; - case 'csisolatingreek': - case 'ecma118': - case 'elot928': - case 'greek': - case 'greek8': - case 'iso88597': - case 'iso885971987': - case 'isoir126': - return 'ISO-8859-7'; + case 'csaz24341985gr': + case 'csiso123csaz24341985gr': + case 'isoir123': + return 'CSA_Z243.4-1985-gr'; - case 'csisolatinhebrew': - case 'hebrew': - case 'iso88598': - case 'iso885981988': - case 'isoir138': - return 'ISO-8859-8'; + case 'csiso139csn369103': + case 'csn369103': + case 'isoir139': + return 'CSN_369103'; - case 'csiso88598e': - case 'iso88598e': - return 'ISO-8859-8-E'; + case 'csdecmcs': + case 'dec': + case 'decmcs': + return 'DEC-MCS'; - case 'csiso88598i': - case 'iso88598i': - return 'ISO-8859-8-I'; + case 'csiso21german': + case 'de': + case 'din66003': + case 'iso646de': + case 'isoir21': + return 'DIN_66003'; - case 'cswindows31latin5': - case 'iso88599windowslatin5': - return 'ISO-8859-9-Windows-Latin-5'; + case 'csdkus': + case 'dkus': + return 'dk-us'; - case 'csisolatin6': - case 'iso885910': - case 'iso8859101992': - case 'isoir157': - case 'l6': - case 'latin6': - return 'ISO-8859-10'; + case 'csiso646danish': + case 'dk': + case 'ds2089': + case 'iso646dk': + return 'DS_2089'; - case 'iso885913': - return 'ISO-8859-13'; + case 'csibmebcdicatde': + case 'ebcdicatde': + return 'EBCDIC-AT-DE'; - case 'iso885914': - case 'iso8859141998': - case 'isoceltic': - case 'isoir199': - case 'l8': - case 'latin8': - return 'ISO-8859-14'; + case 'csebcdicatdea': + case 'ebcdicatdea': + return 'EBCDIC-AT-DE-A'; - case 'iso885915': - case 'latin9': - return 'ISO-8859-15'; + case 'csebcdiccafr': + case 'ebcdiccafr': + return 'EBCDIC-CA-FR'; - case 'iso885916': - case 'iso8859162001': - case 'isoir226': - case 'l10': - case 'latin10': - return 'ISO-8859-16'; + case 'csebcdicdkno': + case 'ebcdicdkno': + return 'EBCDIC-DK-NO'; - case 'iso10646j1': - return 'ISO-10646-J-1'; + case 'csebcdicdknoa': + case 'ebcdicdknoa': + return 'EBCDIC-DK-NO-A'; - case 'csunicode': - case 'iso10646ucs2': - return 'ISO-10646-UCS-2'; + case 'csebcdices': + case 'ebcdices': + return 'EBCDIC-ES'; - case 'csucs4': - case 'iso10646ucs4': - return 'ISO-10646-UCS-4'; + case 'csebcdicesa': + case 'ebcdicesa': + return 'EBCDIC-ES-A'; - case 'csunicodeascii': - case 'iso10646ucsbasic': - return 'ISO-10646-UCS-Basic'; + case 'csebcdicess': + case 'ebcdicess': + return 'EBCDIC-ES-S'; - case 'csunicodelatin1': - case 'iso10646': - case 'iso10646unicodelatin1': - return 'ISO-10646-Unicode-Latin1'; + case 'csebcdicfise': + case 'ebcdicfise': + return 'EBCDIC-FI-SE'; - case 'csiso10646utf1': - case 'iso10646utf1': - return 'ISO-10646-UTF-1'; + case 'csebcdicfisea': + case 'ebcdicfisea': + return 'EBCDIC-FI-SE-A'; - case 'csiso115481': - case 'iso115481': - case 'isotr115481': - return 'ISO-11548-1'; + case 'csebcdicfr': + case 'ebcdicfr': + return 'EBCDIC-FR'; - case 'csiso90': - case 'isoir90': - return 'iso-ir-90'; + case 'csebcdicit': + case 'ebcdicit': + return 'EBCDIC-IT'; - case 'csunicodeibm1261': - case 'isounicodeibm1261': - return 'ISO-Unicode-IBM-1261'; + case 'csebcdicpt': + case 'ebcdicpt': + return 'EBCDIC-PT'; - case 'csunicodeibm1264': - case 'isounicodeibm1264': - return 'ISO-Unicode-IBM-1264'; + case 'csebcdicuk': + case 'ebcdicuk': + return 'EBCDIC-UK'; - case 'csunicodeibm1265': - case 'isounicodeibm1265': - return 'ISO-Unicode-IBM-1265'; + case 'csebcdicus': + case 'ebcdicus': + return 'EBCDIC-US'; - case 'csunicodeibm1268': - case 'isounicodeibm1268': - return 'ISO-Unicode-IBM-1268'; + case 'csiso111ecmacyrillic': + case 'ecmacyrillic': + case 'isoir111': + case 'koi8e': + return 'ECMA-cyrillic'; - case 'csunicodeibm1276': - case 'isounicodeibm1276': - return 'ISO-Unicode-IBM-1276'; + case 'csiso17spanish': + case 'es': + case 'iso646es': + case 'isoir17': + return 'ES'; - case 'csiso646basic1983': - case 'iso646basic1983': - case 'ref': - return 'ISO_646.basic:1983'; + case 'csiso85spanish2': + case 'es2': + case 'iso646es2': + case 'isoir85': + return 'ES2'; - case 'csiso2intlrefversion': - case 'irv': - case 'iso646irv1983': - case 'isoir2': - return 'ISO_646.irv:1983'; - - case 'csiso2033': - case 'e13b': - case 'iso20331983': - case 'isoir98': - return 'ISO_2033-1983'; + case 'cseucpkdfmtjapanese': + case 'eucjp': + case 'extendedunixcodepackedformatforjapanese': + return 'EUC-JP'; - case 'csiso5427cyrillic': - case 'iso5427': - case 'isoir37': - return 'ISO_5427'; + case 'cseucfixwidjapanese': + case 'extendedunixcodefixedwidthforjapanese': + return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; - case 'iso5427cyrillic1981': - case 'iso54271981': - case 'isoir54': - return 'ISO_5427:1981'; + case 'gb18030': + return 'GB18030'; - case 'csiso5428greek': - case 'iso54281980': - case 'isoir55': - return 'ISO_5428:1980'; + case 'chinese': + case 'cp936': + case 'csgb2312': + case 'csiso58gb231280': + case 'gb2312': + case 'gb231280': + case 'gbk': + case 'isoir58': + case 'ms936': + case 'windows936': + return 'GBK'; - case 'csiso6937add': - case 'iso6937225': - case 'isoir152': - return 'ISO_6937-2-25'; + case 'cn': + case 'csiso57gb1988': + case 'gb198880': + case 'iso646cn': + case 'isoir57': + return 'GB_1988-80'; - case 'csisotextcomm': - case 'iso69372add': - case 'isoir142': - return 'ISO_6937-2-add'; + case 'csiso153gost1976874': + case 'gost1976874': + case 'isoir153': + case 'stsev35888': + return 'GOST_19768-74'; - case 'csiso8859supp': - case 'iso8859supp': - case 'isoir154': - case 'latin125': - return 'ISO_8859-supp'; + case 'csiso150': + case 'csiso150greekccitt': + case 'greekccitt': + case 'isoir150': + return 'greek-ccitt'; - case 'csiso10367box': - case 'iso10367box': - case 'isoir155': - return 'ISO_10367-box'; + case 'csiso88greek7': + case 'greek7': + case 'isoir88': + return 'greek7'; - case 'csiso15italian': - case 'iso646it': - case 'isoir15': - case 'it': - return 'IT'; + case 'csiso18greek7old': + case 'greek7old': + case 'isoir18': + return 'greek7-old'; - case 'csiso13jisc6220jp': - case 'isoir13': - case 'jisc62201969': - case 'jisc62201969jp': - case 'katakana': - case 'x2017': - return 'JIS_C6220-1969-jp'; + case 'cshpdesktop': + case 'hpdesktop': + return 'HP-DeskTop'; - case 'csiso14jisc6220ro': - case 'iso646jp': - case 'isoir14': - case 'jisc62201969ro': - case 'jp': - return 'JIS_C6220-1969-ro'; + case 'cshplegal': + case 'hplegal': + return 'HP-Legal'; - case 'csiso42jisc62261978': - case 'isoir42': - case 'jisc62261978': - return 'JIS_C6226-1978'; + case 'cshpmath8': + case 'hpmath8': + return 'HP-Math8'; - case 'csiso87jisx208': - case 'isoir87': - case 'jisc62261983': - case 'jisx2081983': - case 'x208': - return 'JIS_C6226-1983'; + case 'cshppifont': + case 'hppifont': + return 'HP-Pi-font'; - case 'csiso91jisc62291984a': - case 'isoir91': - case 'jisc62291984a': - case 'jpocra': - return 'JIS_C6229-1984-a'; + case 'cshproman8': + case 'hproman8': + case 'r8': + case 'roman8': + return 'hp-roman8'; - case 'csiso92jisc62991984b': - case 'iso646jpocrb': - case 'isoir92': - case 'jisc62291984b': - case 'jpocrb': - return 'JIS_C6229-1984-b'; + case 'hzgb2312': + return 'HZ-GB-2312'; - case 'csiso93jis62291984badd': - case 'isoir93': - case 'jisc62291984badd': - case 'jpocrbadd': - return 'JIS_C6229-1984-b-add'; + case 'csibmsymbols': + case 'ibmsymbols': + return 'IBM-Symbols'; - case 'csiso94jis62291984hand': - case 'isoir94': - case 'jisc62291984hand': - case 'jpocrhand': - return 'JIS_C6229-1984-hand'; + case 'csibmthai': + case 'ibmthai': + return 'IBM-Thai'; - case 'csiso95jis62291984handadd': - case 'isoir95': - case 'jisc62291984handadd': - case 'jpocrhandadd': - return 'JIS_C6229-1984-hand-add'; + case 'cp37': + case 'csibm37': + case 'ebcdiccpca': + case 'ebcdiccpnl': + case 'ebcdiccpus': + case 'ebcdiccpwt': + case 'ibm37': + return 'IBM037'; - case 'csiso96jisc62291984kana': - case 'isoir96': - case 'jisc62291984kana': - return 'JIS_C6229-1984-kana'; + case 'cp38': + case 'csibm38': + case 'ebcdicint': + case 'ibm38': + return 'IBM038'; - case 'csjisencoding': - case 'jisencoding': - return 'JIS_Encoding'; + case 'cp273': + case 'csibm273': + case 'ibm273': + return 'IBM273'; - case 'cshalfwidthkatakana': - case 'jisx201': - case 'x201': - return 'JIS_X0201'; + case 'cp274': + case 'csibm274': + case 'ebcdicbe': + case 'ibm274': + return 'IBM274'; - case 'csiso159jisx2121990': - case 'isoir159': - case 'jisx2121990': - case 'x212': - return 'JIS_X0212-1990'; + case 'cp275': + case 'csibm275': + case 'ebcdicbr': + case 'ibm275': + return 'IBM275'; - case 'csiso141jusib1002': - case 'iso646yu': - case 'isoir141': - case 'js': - case 'jusib1002': - case 'yu': - return 'JUS_I.B1.002'; + case 'csibm277': + case 'ebcdiccpdk': + case 'ebcdiccpno': + case 'ibm277': + return 'IBM277'; - case 'csiso147macedonian': - case 'isoir147': - case 'jusib1003mac': - case 'macedonian': - return 'JUS_I.B1.003-mac'; + case 'cp278': + case 'csibm278': + case 'ebcdiccpfi': + case 'ebcdiccpse': + case 'ibm278': + return 'IBM278'; - case 'csiso146serbian': - case 'isoir146': - case 'jusib1003serb': - case 'serbian': - return 'JUS_I.B1.003-serb'; + case 'cp280': + case 'csibm280': + case 'ebcdiccpit': + case 'ibm280': + return 'IBM280'; - case 'koi7switched': - return 'KOI7-switched'; + case 'cp281': + case 'csibm281': + case 'ebcdicjpe': + case 'ibm281': + return 'IBM281'; - case 'cskoi8r': - case 'koi8r': - return 'KOI8-R'; + case 'cp284': + case 'csibm284': + case 'ebcdiccpes': + case 'ibm284': + return 'IBM284'; - case 'koi8u': - return 'KOI8-U'; + case 'cp285': + case 'csibm285': + case 'ebcdiccpgb': + case 'ibm285': + return 'IBM285'; - case 'csksc5636': - case 'iso646kr': - case 'ksc5636': - return 'KSC5636'; + case 'cp290': + case 'csibm290': + case 'ebcdicjpkana': + case 'ibm290': + return 'IBM290'; - case 'cskz1048': - case 'kz1048': - case 'rk1048': - case 'strk10482002': - return 'KZ-1048'; + case 'cp297': + case 'csibm297': + case 'ebcdiccpfr': + case 'ibm297': + return 'IBM297'; - case 'csiso19latingreek': - case 'isoir19': - case 'latingreek': - return 'latin-greek'; + case 'cp420': + case 'csibm420': + case 'ebcdiccpar1': + case 'ibm420': + return 'IBM420'; - case 'csiso27latingreek1': - case 'isoir27': - case 'latingreek1': - return 'Latin-greek-1'; + case 'cp423': + case 'csibm423': + case 'ebcdiccpgr': + case 'ibm423': + return 'IBM423'; - case 'csiso158lap': - case 'isoir158': - case 'lap': - case 'latinlap': - return 'latin-lap'; + case 'cp424': + case 'csibm424': + case 'ebcdiccphe': + case 'ibm424': + return 'IBM424'; - case 'csmacintosh': - case 'mac': - case 'macintosh': - return 'macintosh'; + case '437': + case 'cp437': + case 'cspc8codepage437': + case 'ibm437': + return 'IBM437'; - case 'csmicrosoftpublishing': - case 'microsoftpublishing': - return 'Microsoft-Publishing'; + case 'cp500': + case 'csibm500': + case 'ebcdiccpbe': + case 'ebcdiccpch': + case 'ibm500': + return 'IBM500'; - case 'csmnem': - case 'mnem': - return 'MNEM'; + case 'cp775': + case 'cspc775baltic': + case 'ibm775': + return 'IBM775'; - case 'csmnemonic': - case 'mnemonic': - return 'MNEMONIC'; + case '850': + case 'cp850': + case 'cspc850multilingual': + case 'ibm850': + return 'IBM850'; - case 'csiso86hungarian': - case 'hu': - case 'iso646hu': - case 'isoir86': - case 'msz77953': - return 'MSZ_7795.3'; + case '851': + case 'cp851': + case 'csibm851': + case 'ibm851': + return 'IBM851'; - case 'csnatsdano': - case 'isoir91': - case 'natsdano': - return 'NATS-DANO'; + case '852': + case 'cp852': + case 'cspcp852': + case 'ibm852': + return 'IBM852'; - case 'csnatsdanoadd': - case 'isoir92': - case 'natsdanoadd': - return 'NATS-DANO-ADD'; + case '855': + case 'cp855': + case 'csibm855': + case 'ibm855': + return 'IBM855'; - case 'csnatssefi': - case 'isoir81': - case 'natssefi': - return 'NATS-SEFI'; + case '857': + case 'cp857': + case 'csibm857': + case 'ibm857': + return 'IBM857'; - case 'csnatssefiadd': - case 'isoir82': - case 'natssefiadd': - return 'NATS-SEFI-ADD'; + case 'ccsid858': + case 'cp858': + case 'ibm858': + case 'pcmultilingual850euro': + return 'IBM00858'; - case 'csiso151cuba': - case 'cuba': - case 'iso646cu': - case 'isoir151': - case 'ncnc1081': - return 'NC_NC00-10:81'; + case '860': + case 'cp860': + case 'csibm860': + case 'ibm860': + return 'IBM860'; - case 'csiso69french': - case 'fr': - case 'iso646fr': - case 'isoir69': - case 'nfz62010': - return 'NF_Z_62-010'; + case '861': + case 'cp861': + case 'cpis': + case 'csibm861': + case 'ibm861': + return 'IBM861'; - case 'csiso25french': - case 'iso646fr1': - case 'isoir25': - case 'nfz620101973': - return 'NF_Z_62-010_(1973)'; + case '862': + case 'cp862': + case 'cspc862latinhebrew': + case 'ibm862': + return 'IBM862'; - case 'csiso60danishnorwegian': - case 'csiso60norwegian1': - case 'iso646no': - case 'isoir60': - case 'no': - case 'ns45511': - return 'NS_4551-1'; + case '863': + case 'cp863': + case 'csibm863': + case 'ibm863': + return 'IBM863'; - case 'csiso61norwegian2': - case 'iso646no2': - case 'isoir61': - case 'no2': - case 'ns45512': - return 'NS_4551-2'; + case 'cp864': + case 'csibm864': + case 'ibm864': + return 'IBM864'; - case 'osdebcdicdf3irv': - return 'OSD_EBCDIC_DF03_IRV'; + case '865': + case 'cp865': + case 'csibm865': + case 'ibm865': + return 'IBM865'; - case 'osdebcdicdf41': - return 'OSD_EBCDIC_DF04_1'; + case '866': + case 'cp866': + case 'csibm866': + case 'ibm866': + return 'IBM866'; - case 'osdebcdicdf415': - return 'OSD_EBCDIC_DF04_15'; + case 'cp868': + case 'cpar': + case 'csibm868': + case 'ibm868': + return 'IBM868'; - case 'cspc8danishnorwegian': - case 'pc8danishnorwegian': - return 'PC8-Danish-Norwegian'; + case '869': + case 'cp869': + case 'cpgr': + case 'csibm869': + case 'ibm869': + return 'IBM869'; - case 'cspc8turkish': - case 'pc8turkish': - return 'PC8-Turkish'; + case 'cp870': + case 'csibm870': + case 'ebcdiccproece': + case 'ebcdiccpyu': + case 'ibm870': + return 'IBM870'; - case 'csiso16portuguese': - case 'iso646pt': - case 'isoir16': - case 'pt': - return 'PT'; + case 'cp871': + case 'csibm871': + case 'ebcdiccpis': + case 'ibm871': + return 'IBM871'; - case 'csiso84portuguese2': - case 'iso646pt2': - case 'isoir84': - case 'pt2': - return 'PT2'; + case 'cp880': + case 'csibm880': + case 'ebcdiccyrillic': + case 'ibm880': + return 'IBM880'; - case 'cp154': - case 'csptcp154': - case 'cyrillicasian': - case 'pt154': - case 'ptcp154': - return 'PTCP154'; + case 'cp891': + case 'csibm891': + case 'ibm891': + return 'IBM891'; - case 'scsu': - return 'SCSU'; - - case 'csiso10swedish': - case 'fi': - case 'iso646fi': - case 'iso646se': - case 'isoir10': - case 'se': - case 'sen850200b': - return 'SEN_850200_B'; + case 'cp903': + case 'csibm903': + case 'ibm903': + return 'IBM903'; - case 'csiso11swedishfornames': - case 'iso646se2': - case 'isoir11': - case 'se2': - case 'sen850200c': - return 'SEN_850200_C'; + case '904': + case 'cp904': + case 'csibbm904': + case 'ibm904': + return 'IBM904'; - case 'csiso102t617bit': - case 'isoir102': - case 't617bit': - return 'T.61-7bit'; + case 'cp905': + case 'csibm905': + case 'ebcdiccptr': + case 'ibm905': + return 'IBM905'; - case 'csiso103t618bit': - case 'isoir103': - case 't61': - case 't618bit': - return 'T.61-8bit'; + case 'cp918': + case 'csibm918': + case 'ebcdiccpar2': + case 'ibm918': + return 'IBM918'; - case 'csiso128t101g2': - case 'isoir128': - case 't101g2': - return 'T.101-G2'; + case 'ccsid924': + case 'cp924': + case 'ebcdiclatin9euro': + case 'ibm924': + return 'IBM00924'; - case 'cstscii': - case 'tscii': - return 'TSCII'; + case 'cp1026': + case 'csibm1026': + case 'ibm1026': + return 'IBM1026'; - case 'csunicode11': - case 'unicode11': - return 'UNICODE-1-1'; + case 'ibm1047': + return 'IBM1047'; - case 'csunicode11utf7': - case 'unicode11utf7': - return 'UNICODE-1-1-UTF-7'; + case 'ccsid1140': + case 'cp1140': + case 'ebcdicus37euro': + case 'ibm1140': + return 'IBM01140'; - case 'csunknown8bit': - case 'unknown8bit': - return 'UNKNOWN-8BIT'; + case 'ccsid1141': + case 'cp1141': + case 'ebcdicde273euro': + case 'ibm1141': + return 'IBM01141'; - case 'ansix341968': - case 'ansix341986': - case 'ascii': - case 'cp367': - case 'csascii': - case 'ibm367': - case 'iso646irv1991': - case 'iso646us': - case 'isoir6': - case 'us': - case 'usascii': - return 'US-ASCII'; + case 'ccsid1142': + case 'cp1142': + case 'ebcdicdk277euro': + case 'ebcdicno277euro': + case 'ibm1142': + return 'IBM01142'; - case 'csusdk': - case 'usdk': - return 'us-dk'; + case 'ccsid1143': + case 'cp1143': + case 'ebcdicfi278euro': + case 'ebcdicse278euro': + case 'ibm1143': + return 'IBM01143'; - case 'utf7': - return 'UTF-7'; + case 'ccsid1144': + case 'cp1144': + case 'ebcdicit280euro': + case 'ibm1144': + return 'IBM01144'; - case 'utf8': - return 'UTF-8'; + case 'ccsid1145': + case 'cp1145': + case 'ebcdices284euro': + case 'ibm1145': + return 'IBM01145'; - case 'utf16': - return 'UTF-16'; + case 'ccsid1146': + case 'cp1146': + case 'ebcdicgb285euro': + case 'ibm1146': + return 'IBM01146'; - case 'utf16be': - return 'UTF-16BE'; + case 'ccsid1147': + case 'cp1147': + case 'ebcdicfr297euro': + case 'ibm1147': + return 'IBM01147'; - case 'utf16le': - return 'UTF-16LE'; + case 'ccsid1148': + case 'cp1148': + case 'ebcdicinternational500euro': + case 'ibm1148': + return 'IBM01148'; - case 'utf32': - return 'UTF-32'; + case 'ccsid1149': + case 'cp1149': + case 'ebcdicis871euro': + case 'ibm1149': + return 'IBM01149'; - case 'utf32be': - return 'UTF-32BE'; + case 'csiso143iecp271': + case 'iecp271': + case 'isoir143': + return 'IEC_P27-1'; - case 'utf32le': - return 'UTF-32LE'; + case 'csiso49inis': + case 'inis': + case 'isoir49': + return 'INIS'; - case 'csventurainternational': - case 'venturainternational': - return 'Ventura-International'; + case 'csiso50inis8': + case 'inis8': + case 'isoir50': + return 'INIS-8'; - case 'csventuramath': - case 'venturamath': - return 'Ventura-Math'; + case 'csiso51iniscyrillic': + case 'iniscyrillic': + case 'isoir51': + return 'INIS-cyrillic'; - case 'csventuraus': - case 'venturaus': - return 'Ventura-US'; + case 'csinvariant': + case 'invariant': + return 'INVARIANT'; - case 'csiso70videotexsupp1': - case 'isoir70': - case 'videotexsuppl': - return 'videotex-suppl'; + case 'iso2022cn': + return 'ISO-2022-CN'; - case 'csviqr': - case 'viqr': - return 'VIQR'; + case 'iso2022cnext': + return 'ISO-2022-CN-EXT'; - case 'csviscii': - case 'viscii': - return 'VISCII'; + case 'csiso2022jp': + case 'iso2022jp': + return 'ISO-2022-JP'; - case 'csshiftjis': - case 'cswindows31j': - case 'mskanji': - case 'shiftjis': - case 'windows31j': - return 'Windows-31J'; + case 'csiso2022jp2': + case 'iso2022jp2': + return 'ISO-2022-JP-2'; - case 'iso885911': - case 'tis620': - return 'windows-874'; + case 'csiso2022kr': + case 'iso2022kr': + return 'ISO-2022-KR'; - case 'cseuckr': - case 'csksc56011987': - case 'euckr': - case 'isoir149': - case 'korean': - case 'ksc5601': - case 'ksc56011987': - case 'ksc56011989': - case 'windows949': - return 'windows-949'; + case 'cswindows30latin1': + case 'iso88591windows30latin1': + return 'ISO-8859-1-Windows-3.0-Latin-1'; - case 'windows1250': - return 'windows-1250'; + case 'cswindows31latin1': + case 'iso88591windows31latin1': + return 'ISO-8859-1-Windows-3.1-Latin-1'; - case 'windows1251': - return 'windows-1251'; + case 'csisolatin2': + case 'iso88592': + case 'iso885921987': + case 'isoir101': + case 'l2': + case 'latin2': + return 'ISO-8859-2'; - case 'cp819': - case 'csisolatin1': - case 'ibm819': - case 'iso88591': - case 'iso885911987': - case 'isoir100': - case 'l1': - case 'latin1': - case 'windows1252': - return 'windows-1252'; + case 'cswindows31latin2': + case 'iso88592windowslatin2': + return 'ISO-8859-2-Windows-Latin-2'; - case 'windows1253': - return 'windows-1253'; + case 'csisolatin3': + case 'iso88593': + case 'iso885931988': + case 'isoir109': + case 'l3': + case 'latin3': + return 'ISO-8859-3'; - case 'csisolatin5': - case 'iso88599': - case 'iso885991989': - case 'isoir148': - case 'l5': - case 'latin5': - case 'windows1254': - return 'windows-1254'; + case 'csisolatin4': + case 'iso88594': + case 'iso885941988': + case 'isoir110': + case 'l4': + case 'latin4': + return 'ISO-8859-4'; - case 'windows1255': - return 'windows-1255'; + case 'csisolatincyrillic': + case 'cyrillic': + case 'iso88595': + case 'iso885951988': + case 'isoir144': + return 'ISO-8859-5'; - case 'windows1256': - return 'windows-1256'; + case 'arabic': + case 'asmo708': + case 'csisolatinarabic': + case 'ecma114': + case 'iso88596': + case 'iso885961987': + case 'isoir127': + return 'ISO-8859-6'; - case 'windows1257': - return 'windows-1257'; + case 'csiso88596e': + case 'iso88596e': + return 'ISO-8859-6-E'; - case 'windows1258': - return 'windows-1258'; + case 'csiso88596i': + case 'iso88596i': + return 'ISO-8859-6-I'; - default: - return $charset; - } - } + case 'csisolatingreek': + case 'ecma118': + case 'elot928': + case 'greek': + case 'greek8': + case 'iso88597': + case 'iso885971987': + case 'isoir126': + return 'ISO-8859-7'; - function get_curl_version() - { - if (is_array($curl = curl_version())) - { - $curl = $curl['version']; - } - elseif (substr($curl, 0, 5) === 'curl/') - { - $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); - } - elseif (substr($curl, 0, 8) === 'libcurl/') - { - $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); - } - else - { - $curl = 0; - } - return $curl; - } + case 'csisolatinhebrew': + case 'hebrew': + case 'iso88598': + case 'iso885981988': + case 'isoir138': + return 'ISO-8859-8'; - function is_subclass_of($class1, $class2) - { - if (func_num_args() !== 2) - { - trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); - } - elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1)) - { - return is_subclass_of($class1, $class2); - } - elseif (is_string($class1) && is_string($class2)) - { - if (class_exists($class1)) - { - if (class_exists($class2)) - { - $class2 = strtolower($class2); - while ($class1 = strtolower(get_parent_class($class1))) - { - if ($class1 === $class2) - { - return true; - } - } - } - } - else - { - trigger_error('Unknown class passed as parameter', E_USER_WARNNG); - } - } - return false; - } + case 'csiso88598e': + case 'iso88598e': + return 'ISO-8859-8-E'; - /** - * Strip HTML comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function strip_comments($data) - { - $output = ''; - while (($start = strpos($data, '<!--')) !== false) - { - $output .= substr($data, 0, $start); - if (($end = strpos($data, '-->', $start)) !== false) - { - $data = substr_replace($data, '', 0, $end + 3); - } - else - { - $data = ''; - } - } - return $output . $data; - } + case 'csiso88598i': + case 'iso88598i': + return 'ISO-8859-8-I'; - function parse_date($dt) - { - $parser = SimplePie_Parse_Date::get(); - return $parser->parse($dt); - } + case 'cswindows31latin5': + case 'iso88599windowslatin5': + return 'ISO-8859-9-Windows-Latin-5'; - /** - * Decode HTML entities - * - * @static - * @access public - * @param string $data Input data - * @return string Output data - */ - function entities_decode($data) - { - $decoder = new SimplePie_Decode_HTML_Entities($data); - return $decoder->parse(); - } + case 'csisolatin6': + case 'iso885910': + case 'iso8859101992': + case 'isoir157': + case 'l6': + case 'latin6': + return 'ISO-8859-10'; - /** - * Remove RFC822 comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function uncomment_rfc822($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; + case 'iso885913': + return 'ISO-8859-13'; - $output = ''; + case 'iso885914': + case 'iso8859141998': + case 'isoceltic': + case 'isoir199': + case 'l8': + case 'latin8': + return 'ISO-8859-14'; - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) - { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') - { - $depth++; - while ($depth && $position < $length) - { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; + case 'iso885915': + case 'latin9': + return 'ISO-8859-15'; - case ')': - $depth--; - break; - } - $position++; - } - else - { - break; - } - } - } - else - { - $output .= '('; - } - } - $output .= substr($string, $position); + case 'iso885916': + case 'iso8859162001': + case 'isoir226': + case 'l10': + case 'latin10': + return 'ISO-8859-16'; - return $output; - } + case 'iso10646j1': + return 'ISO-10646-J-1'; - function parse_mime($mime) - { - if (($pos = strpos($mime, ';')) === false) - { - return trim($mime); - } - else - { - return trim(substr($mime, 0, $pos)); - } - } + case 'csunicode': + case 'iso10646ucs2': + return 'ISO-10646-UCS-2'; - function htmlspecialchars_decode($string, $quote_style) - { - if (function_exists('htmlspecialchars_decode')) - { - return htmlspecialchars_decode($string, $quote_style); - } - else - { - return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); - } - } + case 'csucs4': + case 'iso10646ucs4': + return 'ISO-10646-UCS-4'; - function atom_03_construct_type($attribs) - { - if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64')) - { - $mode = SIMPLEPIE_CONSTRUCT_BASE64; - } - else - { - $mode = SIMPLEPIE_CONSTRUCT_NONE; - } - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - case 'text/plain': - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + case 'csunicodeascii': + case 'iso10646ucsbasic': + return 'ISO-10646-UCS-Basic'; - case 'html': - case 'text/html': - return SIMPLEPIE_CONSTRUCT_HTML | $mode; + case 'csunicodelatin1': + case 'iso10646': + case 'iso10646unicodelatin1': + return 'ISO-10646-Unicode-Latin1'; - case 'xhtml': - case 'application/xhtml+xml': - return SIMPLEPIE_CONSTRUCT_XHTML | $mode; + case 'csiso10646utf1': + case 'iso10646utf1': + return 'ISO-10646-UTF-1'; - default: - return SIMPLEPIE_CONSTRUCT_NONE | $mode; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; - } - } + case 'csiso115481': + case 'iso115481': + case 'isotr115481': + return 'ISO-11548-1'; - function atom_10_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; + case 'csiso90': + case 'isoir90': + return 'iso-ir-90'; - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; + case 'csunicodeibm1261': + case 'isounicodeibm1261': + return 'ISO-Unicode-IBM-1261'; - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; + case 'csunicodeibm1264': + case 'isounicodeibm1264': + return 'ISO-Unicode-IBM-1264'; - default: - return SIMPLEPIE_CONSTRUCT_NONE; - } - } - return SIMPLEPIE_CONSTRUCT_TEXT; - } + case 'csunicodeibm1265': + case 'isounicodeibm1265': + return 'ISO-Unicode-IBM-1265'; - function atom_10_content_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - $type = strtolower(trim($attribs['']['type'])); - switch ($type) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; + case 'csunicodeibm1268': + case 'isounicodeibm1268': + return 'ISO-Unicode-IBM-1268'; - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; + case 'csunicodeibm1276': + case 'isounicodeibm1276': + return 'ISO-Unicode-IBM-1276'; - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; - } - if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/') - { - return SIMPLEPIE_CONSTRUCT_NONE; - } - else - { - return SIMPLEPIE_CONSTRUCT_BASE64; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT; - } - } + case 'csiso646basic1983': + case 'iso646basic1983': + case 'ref': + return 'ISO_646.basic:1983'; - function is_isegment_nz_nc($string) - { - return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); - } + case 'csiso2intlrefversion': + case 'irv': + case 'iso646irv1983': + case 'isoir2': + return 'ISO_646.irv:1983'; - function space_seperated_tokens($string) - { - $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; - $string_length = strlen($string); + case 'csiso2033': + case 'e13b': + case 'iso20331983': + case 'isoir98': + return 'ISO_2033-1983'; - $position = strspn($string, $space_characters); - $tokens = array(); + case 'csiso5427cyrillic': + case 'iso5427': + case 'isoir37': + return 'ISO_5427'; - while ($position < $string_length) - { - $len = strcspn($string, $space_characters, $position); - $tokens[] = substr($string, $position, $len); - $position += $len; - $position += strspn($string, $space_characters, $position); - } + case 'iso5427cyrillic1981': + case 'iso54271981': + case 'isoir54': + return 'ISO_5427:1981'; - return $tokens; - } + case 'csiso5428greek': + case 'iso54281980': + case 'isoir55': + return 'ISO_5428:1980'; - function array_unique($array) - { - if (version_compare(PHP_VERSION, '5.2', '>=')) - { - return array_unique($array); - } - else - { - $array = (array) $array; - $new_array = array(); - $new_array_strings = array(); - foreach ($array as $key => $value) - { - if (is_object($value)) - { - if (method_exists($value, '__toString')) - { - $cmp = $value->__toString(); - } - else - { - trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR); - } - } - elseif (is_array($value)) - { - $cmp = (string) reset($value); - } - else - { - $cmp = (string) $value; - } - if (!in_array($cmp, $new_array_strings)) - { - $new_array[$key] = $value; - $new_array_strings[] = $cmp; - } - } - return $new_array; - } - } + case 'csiso6937add': + case 'iso6937225': + case 'isoir152': + return 'ISO_6937-2-25'; - /** - * Converts a unicode codepoint to a UTF-8 character - * - * @static - * @access public - * @param int $codepoint Unicode codepoint - * @return string UTF-8 character - */ - function codepoint_to_utf8($codepoint) - { - $codepoint = (int) $codepoint; - if ($codepoint < 0) - { - return false; - } - else if ($codepoint <= 0x7f) - { - return chr($codepoint); - } - else if ($codepoint <= 0x7ff) - { - return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0xffff) - { - return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0x10ffff) - { - return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else - { - // U+FFFD REPLACEMENT CHARACTER - return "\xEF\xBF\xBD"; - } - } + case 'csisotextcomm': + case 'iso69372add': + case 'isoir142': + return 'ISO_6937-2-add'; - /** - * Re-implementation of PHP 5's stripos() - * - * Returns the numeric position of the first occurrence of needle in the - * haystack string. - * - * @static - * @access string - * @param object $haystack - * @param string $needle Note that the needle may be a string of one or more - * characters. If needle is not a string, it is converted to an integer - * and applied as the ordinal value of a character. - * @param int $offset The optional offset parameter allows you to specify which - * character in haystack to start searching. The position returned is still - * relative to the beginning of haystack. - * @return bool If needle is not found, stripos() will return boolean false. - */ - function stripos($haystack, $needle, $offset = 0) - { - if (function_exists('stripos')) - { - return stripos($haystack, $needle, $offset); - } - else - { - if (is_string($needle)) - { - $needle = strtolower($needle); - } - elseif (is_int($needle) || is_bool($needle) || is_double($needle)) - { - $needle = strtolower(chr($needle)); - } - else - { - trigger_error('needle is not a string or an integer', E_USER_WARNING); - return false; - } + case 'csiso8859supp': + case 'iso8859supp': + case 'isoir154': + case 'latin125': + return 'ISO_8859-supp'; - return strpos(strtolower($haystack), $needle, $offset); - } - } + case 'csiso10367box': + case 'iso10367box': + case 'isoir155': + return 'ISO_10367-box'; - /** - * Similar to parse_str() - * - * Returns an associative array of name/value pairs, where the value is an - * array of values that have used the same name - * - * @static - * @access string - * @param string $str The input string. - * @return array - */ - function parse_str($str) - { - $return = array(); - $str = explode('&', $str); + case 'csiso15italian': + case 'iso646it': + case 'isoir15': + case 'it': + return 'IT'; - foreach ($str as $section) - { - if (strpos($section, '=') !== false) - { - list($name, $value) = explode('=', $section, 2); - $return[urldecode($name)][] = urldecode($value); - } - else - { - $return[urldecode($section)][] = null; - } - } + case 'csiso13jisc6220jp': + case 'isoir13': + case 'jisc62201969': + case 'jisc62201969jp': + case 'katakana': + case 'x2017': + return 'JIS_C6220-1969-jp'; - return $return; - } + case 'csiso14jisc6220ro': + case 'iso646jp': + case 'isoir14': + case 'jisc62201969ro': + case 'jp': + return 'JIS_C6220-1969-ro'; - /** - * Detect XML encoding, as per XML 1.0 Appendix F.1 - * - * @todo Add support for EBCDIC - * @param string $data XML data - * @return array Possible encodings - */ - function xml_encoding($data) - { - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") - { - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") - { - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") - { - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") - { - $encoding[] = 'UTF-16LE'; - } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") - { - $encoding[] = 'UTF-8'; - } - // UTF-32 Big Endian Without BOM - elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") - { - if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian Without BOM - elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") - { - if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian Without BOM - elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") - { - if ($pos = strpos($data, "\x00\x3F\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian Without BOM - elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") - { - if ($pos = strpos($data, "\x3F\x00\x3E\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16LE'; - } - // US-ASCII (or superset) - elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") - { - if ($pos = strpos($data, "\x3F\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-8'; - } - // Fallback to UTF-8 - else - { - $encoding[] = 'UTF-8'; - } - return $encoding; - } + case 'csiso42jisc62261978': + case 'isoir42': + case 'jisc62261978': + return 'JIS_C6226-1978'; - function output_javascript() - { - if (function_exists('ob_gzhandler')) - { - ob_start('ob_gzhandler'); - } - header('Content-type: text/javascript; charset: UTF-8'); - header('Cache-Control: must-revalidate'); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - ?> -function embed_odeo(link) { - document.writeln('<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url='+link+'"></embed>'); -} + case 'csiso87jisx208': + case 'isoir87': + case 'jisc62261983': + case 'jisx2081983': + case 'x208': + return 'JIS_C6226-1983'; -function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { - if (placeholder != '') { - document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); - } - else { - document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); - } -} + case 'csiso91jisc62291984a': + case 'isoir91': + case 'jisc62291984a': + case 'jpocra': + return 'JIS_C6229-1984-a'; -function embed_flash(bgcolor, width, height, link, loop, type) { - document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>'); -} + case 'csiso92jisc62991984b': + case 'iso646jpocrb': + case 'isoir92': + case 'jisc62291984b': + case 'jpocrb': + return 'JIS_C6229-1984-b'; -function embed_flv(width, height, link, placeholder, loop, player) { - document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>'); -} + case 'csiso93jis62291984badd': + case 'isoir93': + case 'jisc62291984badd': + case 'jpocrbadd': + return 'JIS_C6229-1984-b-add'; -function embed_wmedia(width, height, link) { - document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); -} - <?php - } + case 'csiso94jis62291984hand': + case 'isoir94': + case 'jisc62291984hand': + case 'jpocrhand': + return 'JIS_C6229-1984-hand'; + case 'csiso95jis62291984handadd': + case 'isoir95': + case 'jisc62291984handadd': + case 'jpocrhandadd': + return 'JIS_C6229-1984-hand-add'; + case 'csiso96jisc62291984kana': + case 'isoir96': + case 'jisc62291984kana': + return 'JIS_C6229-1984-kana'; - /** - * Format debugging information - */ - function debug($sp) - { - $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n"; - $info .= 'PHP ' . PHP_VERSION . "\n"; - if ($sp->error() !== null) - { - $info .= 'Error occurred: ' . $sp->error() . "\n"; - } - else - { - $info .= "No error found.\n"; - } - $info .= "Extensions:\n"; - $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'); - foreach ($extensions as $ext) - { - if (extension_loaded($ext)) - { - $info .= " $ext loaded\n"; - switch ($ext) - { - case 'pcre': - $info .= ' Version ' . PCRE_VERSION . "\n"; - break; - case 'curl': - $version = curl_version(); - $info .= ' Version ' . $version['version'] . "\n"; - break; - case 'mbstring': - $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n"; - break; - case 'iconv': - $info .= ' Version ' . ICONV_VERSION . "\n"; - break; - case 'xml': - $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n"; - break; - } - } - else - { - $info .= " $ext not loaded\n"; - } - } - return $info; - } -} + case 'csjisencoding': + case 'jisencoding': + return 'JIS_Encoding'; -/** - * Decode HTML Entities - * - * This implements HTML5 as of revision 967 (2007-06-28) - * - * @package SimplePie - */ -class SimplePie_Decode_HTML_Entities -{ - /** - * Data to be parsed - * - * @access private - * @var string - */ - var $data = ''; + case 'cshalfwidthkatakana': + case 'jisx201': + case 'x201': + return 'JIS_X0201'; - /** - * Currently consumed bytes - * - * @access private - * @var string - */ - var $consumed = ''; + case 'csiso159jisx2121990': + case 'isoir159': + case 'jisx2121990': + case 'x212': + return 'JIS_X0212-1990'; - /** - * Position of the current byte being parsed - * - * @access private - * @var int - */ - var $position = 0; + case 'csiso141jusib1002': + case 'iso646yu': + case 'isoir141': + case 'js': + case 'jusib1002': + case 'yu': + return 'JUS_I.B1.002'; - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_Decode_HTML_Entities($data) - { - $this->data = $data; - } + case 'csiso147macedonian': + case 'isoir147': + case 'jusib1003mac': + case 'macedonian': + return 'JUS_I.B1.003-mac'; - /** - * Parse the input data - * - * @access public - * @return string Output data - */ - function parse() - { - while (($this->position = strpos($this->data, '&', $this->position)) !== false) - { - $this->consume(); - $this->entity(); - $this->consumed = ''; - } - return $this->data; - } + case 'csiso146serbian': + case 'isoir146': + case 'jusib1003serb': + case 'serbian': + return 'JUS_I.B1.003-serb'; - /** - * Consume the next byte - * - * @access private - * @return mixed The next byte, or false, if there is no more data - */ - function consume() - { - if (isset($this->data[$this->position])) - { - $this->consumed .= $this->data[$this->position]; - return $this->data[$this->position++]; - } - else - { - return false; - } - } + case 'koi7switched': + return 'KOI7-switched'; - /** - * Consume a range of characters - * - * @access private - * @param string $chars Characters to consume - * @return mixed A series of characters that match the range, or false - */ - function consume_range($chars) - { - if ($len = strspn($this->data, $chars, $this->position)) - { - $data = substr($this->data, $this->position, $len); - $this->consumed .= $data; - $this->position += $len; - return $data; - } - else - { - return false; - } - } + case 'cskoi8r': + case 'koi8r': + return 'KOI8-R'; - /** - * Unconsume one byte - * - * @access private - */ - function unconsume() - { - $this->consumed = substr($this->consumed, 0, -1); - $this->position--; - } + case 'koi8u': + return 'KOI8-U'; - /** - * Decode an entity - * - * @access private - */ - function entity() - { - switch ($this->consume()) - { - case "\x09": - case "\x0A": - case "\x0B": - case "\x0B": - case "\x0C": - case "\x20": - case "\x3C": - case "\x26": - case false: - break; + case 'csksc5636': + case 'iso646kr': + case 'ksc5636': + return 'KSC5636'; - case "\x23": - switch ($this->consume()) - { - case "\x78": - case "\x58": - $range = '0123456789ABCDEFabcdef'; - $hex = true; - break; + case 'cskz1048': + case 'kz1048': + case 'rk1048': + case 'strk10482002': + return 'KZ-1048'; - default: - $range = '0123456789'; - $hex = false; - $this->unconsume(); - break; - } + case 'csiso19latingreek': + case 'isoir19': + case 'latingreek': + return 'latin-greek'; - if ($codepoint = $this->consume_range($range)) - { - static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); + case 'csiso27latingreek1': + case 'isoir27': + case 'latingreek1': + return 'Latin-greek-1'; - if ($hex) - { - $codepoint = hexdec($codepoint); - } - else - { - $codepoint = intval($codepoint); - } + case 'csiso158lap': + case 'isoir158': + case 'lap': + case 'latinlap': + return 'latin-lap'; - if (isset($windows_1252_specials[$codepoint])) - { - $replacement = $windows_1252_specials[$codepoint]; - } - else - { - $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); - } + case 'csmacintosh': + case 'mac': + case 'macintosh': + return 'macintosh'; - if (!in_array($this->consume(), array(';', false), true)) - { - $this->unconsume(); - } + case 'csmicrosoftpublishing': + case 'microsoftpublishing': + return 'Microsoft-Publishing'; - $consumed_length = strlen($this->consumed); - $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); - $this->position += strlen($replacement) - $consumed_length; - } - break; + case 'csmnem': + case 'mnem': + return 'MNEM'; - default: - static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C"); + case 'csmnemonic': + case 'mnemonic': + return 'MNEMONIC'; - for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) - { - $consumed = substr($this->consumed, 1); - if (isset($entities[$consumed])) - { - $match = $consumed; - } - } + case 'csiso86hungarian': + case 'hu': + case 'iso646hu': + case 'isoir86': + case 'msz77953': + return 'MSZ_7795.3'; - if ($match !== null) - { - $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); - $this->position += strlen($entities[$match]) - strlen($consumed) - 1; - } - break; - } - } -} + case 'csnatsdano': + case 'isoir91': + case 'natsdano': + return 'NATS-DANO'; -/** - * IRI parser/serialiser - * - * @package SimplePie - */ -class SimplePie_IRI -{ - /** - * Scheme - * - * @access private - * @var string - */ - var $scheme; + case 'csnatsdanoadd': + case 'isoir92': + case 'natsdanoadd': + return 'NATS-DANO-ADD'; - /** - * User Information - * - * @access private - * @var string - */ - var $userinfo; + case 'csnatssefi': + case 'isoir81': + case 'natssefi': + return 'NATS-SEFI'; - /** - * Host - * - * @access private - * @var string - */ - var $host; + case 'csnatssefiadd': + case 'isoir82': + case 'natssefiadd': + return 'NATS-SEFI-ADD'; - /** - * Port - * - * @access private - * @var string - */ - var $port; + case 'csiso151cuba': + case 'cuba': + case 'iso646cu': + case 'isoir151': + case 'ncnc1081': + return 'NC_NC00-10:81'; - /** - * Path - * - * @access private - * @var string - */ - var $path; + case 'csiso69french': + case 'fr': + case 'iso646fr': + case 'isoir69': + case 'nfz62010': + return 'NF_Z_62-010'; - /** - * Query - * - * @access private - * @var string - */ - var $query; + case 'csiso25french': + case 'iso646fr1': + case 'isoir25': + case 'nfz620101973': + return 'NF_Z_62-010_(1973)'; - /** - * Fragment - * - * @access private - * @var string - */ - var $fragment; + case 'csiso60danishnorwegian': + case 'csiso60norwegian1': + case 'iso646no': + case 'isoir60': + case 'no': + case 'ns45511': + return 'NS_4551-1'; - /** - * Whether the object represents a valid IRI - * - * @access private - * @var array - */ - var $valid = array(); + case 'csiso61norwegian2': + case 'iso646no2': + case 'isoir61': + case 'no2': + case 'ns45512': + return 'NS_4551-2'; - /** - * Return the entire IRI when you try and read the object as a string - * - * @access public - * @return string - */ - function __toString() - { - return $this->get_iri(); - } + case 'osdebcdicdf3irv': + return 'OSD_EBCDIC_DF03_IRV'; - /** - * Create a new IRI object, from a specified string - * - * @access public - * @param string $iri - * @return SimplePie_IRI - */ - function SimplePie_IRI($iri) - { - $iri = (string) $iri; - if ($iri !== '') - { - $parsed = $this->parse_iri($iri); - $this->set_scheme($parsed['scheme']); - $this->set_authority($parsed['authority']); - $this->set_path($parsed['path']); - $this->set_query($parsed['query']); - $this->set_fragment($parsed['fragment']); - } - } + case 'osdebcdicdf41': + return 'OSD_EBCDIC_DF04_1'; - /** - * Create a new IRI object by resolving a relative IRI - * - * @static - * @access public - * @param SimplePie_IRI $base Base IRI - * @param string $relative Relative IRI - * @return SimplePie_IRI - */ - function absolutize($base, $relative) - { - $relative = (string) $relative; - if ($relative !== '') - { - $relative = new SimplePie_IRI($relative); - if ($relative->get_scheme() !== null) - { - $target = $relative; - } - elseif ($base->get_iri() !== null) - { - if ($relative->get_authority() !== null) - { - $target = $relative; - $target->set_scheme($base->get_scheme()); - } - else - { - $target = new SimplePie_IRI(''); - $target->set_scheme($base->get_scheme()); - $target->set_userinfo($base->get_userinfo()); - $target->set_host($base->get_host()); - $target->set_port($base->get_port()); - if ($relative->get_path() !== null) - { - if (strpos($relative->get_path(), '/') === 0) - { - $target->set_path($relative->get_path()); - } - elseif (($base->get_userinfo() !== null || $base->get_host() !== null || $base->get_port() !== null) && $base->get_path() === null) - { - $target->set_path('/' . $relative->get_path()); - } - elseif (($last_segment = strrpos($base->get_path(), '/')) !== false) - { - $target->set_path(substr($base->get_path(), 0, $last_segment + 1) . $relative->get_path()); - } - else - { - $target->set_path($relative->get_path()); - } - $target->set_query($relative->get_query()); - } - else - { - $target->set_path($base->get_path()); - if ($relative->get_query() !== null) - { - $target->set_query($relative->get_query()); - } - elseif ($base->get_query() !== null) - { - $target->set_query($base->get_query()); - } - } - } - $target->set_fragment($relative->get_fragment()); - } - else - { - // No base URL, just return the relative URL - $target = $relative; - } - } - else - { - $target = $base; - } - return $target; - } + case 'osdebcdicdf415': + return 'OSD_EBCDIC_DF04_15'; - /** - * Parse an IRI into scheme/authority/path/query/fragment segments - * - * @access private - * @param string $iri - * @return array - */ - function parse_iri($iri) - { - preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $iri, $match); - for ($i = count($match); $i <= 9; $i++) - { - $match[$i] = ''; - } - return array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]); - } + case 'cspc8danishnorwegian': + case 'pc8danishnorwegian': + return 'PC8-Danish-Norwegian'; - /** - * Remove dot segments from a path - * - * @access private - * @param string $input - * @return string - */ - function remove_dot_segments($input) - { - $output = ''; - while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') - { - // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, - if (strpos($input, '../') === 0) - { - $input = substr($input, 3); - } - elseif (strpos($input, './') === 0) - { - $input = substr($input, 2); - } - // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, - elseif (strpos($input, '/./') === 0) - { - $input = substr_replace($input, '/', 0, 3); - } - elseif ($input === '/.') - { - $input = '/'; - } - // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, - elseif (strpos($input, '/../') === 0) - { - $input = substr_replace($input, '/', 0, 4); - $output = substr_replace($output, '', strrpos($output, '/')); - } - elseif ($input === '/..') - { - $input = '/'; - $output = substr_replace($output, '', strrpos($output, '/')); - } - // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, - elseif ($input === '.' || $input === '..') - { - $input = ''; - } - // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer - elseif (($pos = strpos($input, '/', 1)) !== false) - { - $output .= substr($input, 0, $pos); - $input = substr_replace($input, '', 0, $pos); - } - else - { - $output .= $input; - $input = ''; - } - } - return $output . $input; - } + case 'cspc8turkish': + case 'pc8turkish': + return 'PC8-Turkish'; - /** - * Replace invalid character with percent encoding - * - * @param string $string Input string - * @param string $valid_chars Valid characters - * @param int $case Normalise case - * @return string - */ - function replace_invalid_with_pct_encoding($string, $valid_chars, $case = SIMPLEPIE_SAME_CASE, $iprivate = false) - { - // Normalize as many pct-encoded sections as possible - $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); + case 'csiso16portuguese': + case 'iso646pt': + case 'isoir16': + case 'pt': + return 'PT'; - // Replace invalid percent characters - $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); + case 'csiso84portuguese2': + case 'iso646pt2': + case 'isoir84': + case 'pt2': + return 'PT2'; - // Add unreserved and % to $valid_chars (the latter is safe because all - // pct-encoded sections are now valid). - $valid_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; + case 'cp154': + case 'csptcp154': + case 'cyrillicasian': + case 'pt154': + case 'ptcp154': + return 'PTCP154'; - // Now replace any bytes that aren't allowed with their pct-encoded versions - $position = 0; - $strlen = strlen($string); - while (($position += strspn($string, $valid_chars, $position)) < $strlen) - { - $value = ord($string[$position]); + case 'scsu': + return 'SCSU'; - // Start position - $start = $position; + case 'csiso10swedish': + case 'fi': + case 'iso646fi': + case 'iso646se': + case 'isoir10': + case 'se': + case 'sen850200b': + return 'SEN_850200_B'; - // By default we are valid - $valid = true; + case 'csiso11swedishfornames': + case 'iso646se2': + case 'isoir11': + case 'se2': + case 'sen850200c': + return 'SEN_850200_C'; - // No one byte sequences are valid due to the while. - // Two byte sequence: - if (($value & 0xE0) === 0xC0) - { - $character = ($value & 0x1F) << 6; - $length = 2; - $remaining = 1; - } - // Three byte sequence: - elseif (($value & 0xF0) === 0xE0) - { - $character = ($value & 0x0F) << 12; - $length = 3; - $remaining = 2; - } - // Four byte sequence: - elseif (($value & 0xF8) === 0xF0) - { - $character = ($value & 0x07) << 18; - $length = 4; - $remaining = 3; - } - // Invalid byte: - else - { - $valid = false; - $length = 1; - $remaining = 0; - } + case 'csiso102t617bit': + case 'isoir102': + case 't617bit': + return 'T.61-7bit'; - if ($remaining) - { - if ($position + $length <= $strlen) - { - for ($position++; $remaining; $position++) - { - $value = ord($string[$position]); + case 'csiso103t618bit': + case 'isoir103': + case 't61': + case 't618bit': + return 'T.61-8bit'; - // Check that the byte is valid, then add it to the character: - if (($value & 0xC0) === 0x80) - { - $character |= ($value & 0x3F) << (--$remaining * 6); - } - // If it is invalid, count the sequence as invalid and reprocess the current byte: - else - { - $valid = false; - $position--; - break; - } - } - } - else - { - $position = $strlen - 1; - $valid = false; - } - } + case 'csiso128t101g2': + case 'isoir128': + case 't101g2': + return 'T.101-G2'; - // Percent encode anything invalid or not in ucschar - if ( - // Invalid sequences - !$valid - // Non-shortest form sequences are invalid - || $length > 1 && $character <= 0x7F - || $length > 2 && $character <= 0x7FF - || $length > 3 && $character <= 0xFFFF - // Outside of range of ucschar codepoints - // Noncharacters - || ($character & 0xFFFE) === 0xFFFE - || $character >= 0xFDD0 && $character <= 0xFDEF - || ( - // Everything else not in ucschar - $character > 0xD7FF && $character < 0xF900 - || $character < 0xA0 - || $character > 0xEFFFD - ) - && ( - // Everything not in iprivate, if it applies - !$iprivate - || $character < 0xE000 - || $character > 0x10FFFD - ) - ) - { - // If we were a character, pretend we weren't, but rather an error. - if ($valid) - $position--; + case 'cstscii': + case 'tscii': + return 'TSCII'; - for ($j = $start; $j <= $position; $j++) - { - $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); - $j += 2; - $position += 2; - $strlen += 2; - } - } - } + case 'csunicode11': + case 'unicode11': + return 'UNICODE-1-1'; - // Normalise case - if ($case & SIMPLEPIE_LOWERCASE) - { - $string = strtolower($string); - } - elseif ($case & SIMPLEPIE_UPPERCASE) - { - $string = strtoupper($string); - } + case 'csunicode11utf7': + case 'unicode11utf7': + return 'UNICODE-1-1-UTF-7'; - return $string; - } + case 'csunknown8bit': + case 'unknown8bit': + return 'UNKNOWN-8BIT'; - /** - * Callback function for preg_replace_callback. - * - * Removes sequences of percent encoded bytes that represent UTF-8 - * encoded characters in iunreserved - * - * @access private - * @param array $match PCRE match - * @return string Replacement - */ - function remove_iunreserved_percent_encoded($match) - { - // As we just have valid percent encoded sequences we can just explode - // and ignore the first member of the returned array (an empty string). - $bytes = explode('%', $match[0]); + case 'ansix341968': + case 'ansix341986': + case 'ascii': + case 'cp367': + case 'csascii': + case 'ibm367': + case 'iso646irv1991': + case 'iso646us': + case 'isoir6': + case 'us': + case 'usascii': + return 'US-ASCII'; - // Initialize the new string (this is what will be returned) and that - // there are no bytes remaining in the current sequence (unsurprising - // at the first byte!). - $string = ''; - $remaining = 0; + case 'csusdk': + case 'usdk': + return 'us-dk'; - // Loop over each and every byte, and set $value to its value - for ($i = 1, $len = count($bytes); $i < $len; $i++) - { - $value = hexdec($bytes[$i]); + case 'utf7': + return 'UTF-7'; - // If we're the first byte of sequence: - if (!$remaining) - { - // Start position - $start = $i; + case 'utf8': + return 'UTF-8'; - // By default we are valid - $valid = true; + case 'utf16': + return 'UTF-16'; - // One byte sequence: - if ($value <= 0x7F) - { - $character = $value; - $length = 1; - } - // Two byte sequence: - elseif (($value & 0xE0) === 0xC0) - { - $character = ($value & 0x1F) << 6; - $length = 2; - $remaining = 1; - } - // Three byte sequence: - elseif (($value & 0xF0) === 0xE0) - { - $character = ($value & 0x0F) << 12; - $length = 3; - $remaining = 2; - } - // Four byte sequence: - elseif (($value & 0xF8) === 0xF0) - { - $character = ($value & 0x07) << 18; - $length = 4; - $remaining = 3; - } - // Invalid byte: - else - { - $valid = false; - $remaining = 0; - } - } - // Continuation byte: - else - { - // Check that the byte is valid, then add it to the character: - if (($value & 0xC0) === 0x80) - { - $remaining--; - $character |= ($value & 0x3F) << ($remaining * 6); - } - // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: - else - { - $valid = false; - $remaining = 0; - $i--; - } - } + case 'utf16be': + return 'UTF-16BE'; - // If we've reached the end of the current byte sequence, append it to Unicode::$data - if (!$remaining) - { - // Percent encode anything invalid or not in iunreserved - if ( - // Invalid sequences - !$valid - // Non-shortest form sequences are invalid - || $length > 1 && $character <= 0x7F - || $length > 2 && $character <= 0x7FF - || $length > 3 && $character <= 0xFFFF - // Outside of range of iunreserved codepoints - || $character < 0x2D - || $character > 0xEFFFD - // Noncharacters - || ($character & 0xFFFE) === 0xFFFE - || $character >= 0xFDD0 && $character <= 0xFDEF - // Everything else not in iunreserved (this is all BMP) - || $character === 0x2F - || $character > 0x39 && $character < 0x41 - || $character > 0x5A && $character < 0x61 - || $character > 0x7A && $character < 0x7E - || $character > 0x7E && $character < 0xA0 - || $character > 0xD7FF && $character < 0xF900 - ) - { - for ($j = $start; $j <= $i; $j++) - { - $string .= '%' . strtoupper($bytes[$j]); - } - } - else - { - for ($j = $start; $j <= $i; $j++) - { - $string .= chr(hexdec($bytes[$j])); - } - } - } - } + case 'utf16le': + return 'UTF-16LE'; - // If we have any bytes left over they are invalid (i.e., we are - // mid-way through a multi-byte sequence) - if ($remaining) - { - for ($j = $start; $j < $len; $j++) - { - $string .= '%' . strtoupper($bytes[$j]); - } - } + case 'utf32': + return 'UTF-32'; - return $string; - } + case 'utf32be': + return 'UTF-32BE'; - /** - * Check if the object represents a valid IRI - * - * @access public - * @return bool - */ - function is_valid() - { - return array_sum($this->valid) === count($this->valid); - } + case 'utf32le': + return 'UTF-32LE'; - /** - * Set the scheme. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $scheme - * @return bool - */ - function set_scheme($scheme) - { - if ($scheme === null || $scheme === '') - { - $this->scheme = null; - } - else - { - $len = strlen($scheme); - switch (true) - { - case $len > 1: - if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-.', 1)) - { - $this->scheme = null; - $this->valid[__FUNCTION__] = false; - return false; - } + case 'csventurainternational': + case 'venturainternational': + return 'Ventura-International'; - case $len > 0: - if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 0, 1)) - { - $this->scheme = null; - $this->valid[__FUNCTION__] = false; - return false; - } - } - $this->scheme = strtolower($scheme); + case 'csventuramath': + case 'venturamath': + return 'Ventura-Math'; + + case 'csventuraus': + case 'venturaus': + return 'Ventura-US'; + + case 'csiso70videotexsupp1': + case 'isoir70': + case 'videotexsuppl': + return 'videotex-suppl'; + + case 'csviqr': + case 'viqr': + return 'VIQR'; + + case 'csviscii': + case 'viscii': + return 'VISCII'; + + case 'csshiftjis': + case 'cswindows31j': + case 'mskanji': + case 'shiftjis': + case 'windows31j': + return 'Windows-31J'; + + case 'iso885911': + case 'tis620': + return 'windows-874'; + + case 'cseuckr': + case 'csksc56011987': + case 'euckr': + case 'isoir149': + case 'korean': + case 'ksc5601': + case 'ksc56011987': + case 'ksc56011989': + case 'windows949': + return 'windows-949'; + + case 'windows1250': + return 'windows-1250'; + + case 'windows1251': + return 'windows-1251'; + + case 'cp819': + case 'csisolatin1': + case 'ibm819': + case 'iso88591': + case 'iso885911987': + case 'isoir100': + case 'l1': + case 'latin1': + case 'windows1252': + return 'windows-1252'; + + case 'windows1253': + return 'windows-1253'; + + case 'csisolatin5': + case 'iso88599': + case 'iso885991989': + case 'isoir148': + case 'l5': + case 'latin5': + case 'windows1254': + return 'windows-1254'; + + case 'windows1255': + return 'windows-1255'; + + case 'windows1256': + return 'windows-1256'; + + case 'windows1257': + return 'windows-1257'; + + case 'windows1258': + return 'windows-1258'; + + default: + return $charset; } - $this->valid[__FUNCTION__] = true; - return true; } - /** - * Set the authority. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $authority - * @return bool - */ - function set_authority($authority) + public static function get_curl_version() { - if (($userinfo_end = strrpos($authority, '@')) !== false) - { - $userinfo = substr($authority, 0, $userinfo_end); - $authority = substr($authority, $userinfo_end + 1); - } - else - { - $userinfo = null; - } - - if (($port_start = strpos($authority, ':')) !== false) + if (is_array($curl = curl_version())) { - $port = substr($authority, $port_start + 1); - $authority = substr($authority, 0, $port_start); + $curl = $curl['version']; } - else + elseif (substr($curl, 0, 5) === 'curl/') { - $port = null; + $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); } - - return $this->set_userinfo($userinfo) && $this->set_host($authority) && $this->set_port($port); - } - - /** - * Set the userinfo. - * - * @access public - * @param string $userinfo - * @return bool - */ - function set_userinfo($userinfo) - { - if ($userinfo === null || $userinfo === '') + elseif (substr($curl, 0, 8) === 'libcurl/') { - $this->userinfo = null; + $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); } else { - $this->userinfo = $this->replace_invalid_with_pct_encoding($userinfo, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:'); + $curl = 0; } - $this->valid[__FUNCTION__] = true; - return true; + return $curl; } /** - * Set the host. Returns true on success, false on failure (if there are - * any invalid characters). + * Strip HTML comments * - * @access public - * @param string $host - * @return bool + * @param string $data Data to strip comments from + * @return string Comment stripped string */ - function set_host($host) + public static function strip_comments($data) { - if ($host === null || $host === '') - { - $this->host = null; - $this->valid[__FUNCTION__] = true; - return true; - } - elseif ($host[0] === '[' && substr($host, -1) === ']') + $output = ''; + while (($start = strpos($data, '<!--')) !== false) { - if (Net_IPv6::checkIPv6(substr($host, 1, -1))) + $output .= substr($data, 0, $start); + if (($end = strpos($data, '-->', $start)) !== false) { - $this->host = $host; - $this->valid[__FUNCTION__] = true; - return true; + $data = substr_replace($data, '', 0, $end + 3); } else { - $this->host = null; - $this->valid[__FUNCTION__] = false; - return false; + $data = ''; } } - else - { - $this->host = $this->replace_invalid_with_pct_encoding($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=', SIMPLEPIE_LOWERCASE); - $this->valid[__FUNCTION__] = true; - return true; - } + return $output . $data; + } + + public static function parse_date($dt) + { + $parser = SimplePie_Parse_Date::get(); + return $parser->parse($dt); } /** - * Set the port. Returns true on success, false on failure (if there are - * any invalid characters). + * Decode HTML entities * - * @access public - * @param string $port - * @return bool - */ - function set_port($port) + * @deprecated Use DOMDocument instead + * @param string $data Input data + * @return string Output data + */ + public static function entities_decode($data) + { + $decoder = new SimplePie_Decode_HTML_Entities($data); + return $decoder->parse(); + } + + /** + * Remove RFC822 comments + * + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + public static function uncomment_rfc822($string) { - if ($port === null || $port === '') + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) { - $this->port = null; - $this->valid[__FUNCTION__] = true; - return true; + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } } - elseif (strspn($port, '0123456789') === strlen($port)) + $output .= substr($string, $position); + + return $output; + } + + public static function parse_mime($mime) + { + if (($pos = strpos($mime, ';')) === false) { - $this->port = (int) $port; - $this->valid[__FUNCTION__] = true; - return true; + return trim($mime); } else { - $this->port = null; - $this->valid[__FUNCTION__] = false; + return trim(substr($mime, 0, $pos)); + } + } + + public static function atom_03_construct_type($attribs) + { + if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64')) + { + $mode = SIMPLEPIE_CONSTRUCT_BASE64; + } + else + { + $mode = SIMPLEPIE_CONSTRUCT_NONE; + } + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + case 'text/plain': + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + + case 'html': + case 'text/html': + return SIMPLEPIE_CONSTRUCT_HTML | $mode; + + case 'xhtml': + case 'application/xhtml+xml': + return SIMPLEPIE_CONSTRUCT_XHTML | $mode; + + default: + return SIMPLEPIE_CONSTRUCT_NONE | $mode; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + } + } + + public static function atom_10_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + + default: + return SIMPLEPIE_CONSTRUCT_NONE; + } + } + return SIMPLEPIE_CONSTRUCT_TEXT; + } + + public static function atom_10_content_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + $type = strtolower(trim($attribs['']['type'])); + switch ($type) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + } + if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/') + { + return SIMPLEPIE_CONSTRUCT_NONE; + } + else + { + return SIMPLEPIE_CONSTRUCT_BASE64; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + public static function is_isegment_nz_nc($string) + { + return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); + } + + public static function space_seperated_tokens($string) + { + $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; + $string_length = strlen($string); + + $position = strspn($string, $space_characters); + $tokens = array(); + + while ($position < $string_length) + { + $len = strcspn($string, $space_characters, $position); + $tokens[] = substr($string, $position, $len); + $position += $len; + $position += strspn($string, $space_characters, $position); + } + + return $tokens; + } + + /** + * Converts a unicode codepoint to a UTF-8 character + * + * @static + * @param int $codepoint Unicode codepoint + * @return string UTF-8 character + */ + public static function codepoint_to_utf8($codepoint) + { + $codepoint = (int) $codepoint; + if ($codepoint < 0) + { return false; } + else if ($codepoint <= 0x7f) + { + return chr($codepoint); + } + else if ($codepoint <= 0x7ff) + { + return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0xffff) + { + return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0x10ffff) + { + return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else + { + // U+FFFD REPLACEMENT CHARACTER + return "\xEF\xBF\xBD"; + } + } + + /** + * Similar to parse_str() + * + * Returns an associative array of name/value pairs, where the value is an + * array of values that have used the same name + * + * @static + * @param string $str The input string. + * @return array + */ + public static function parse_str($str) + { + $return = array(); + $str = explode('&', $str); + + foreach ($str as $section) + { + if (strpos($section, '=') !== false) + { + list($name, $value) = explode('=', $section, 2); + $return[urldecode($name)][] = urldecode($value); + } + else + { + $return[urldecode($section)][] = null; + } + } + + return $return; } /** - * Set the path. + * Detect XML encoding, as per XML 1.0 Appendix F.1 + * + * @todo Add support for EBCDIC + * @param string $data XML data + * @param SimplePie_Registry $registry Class registry + * @return array Possible encodings + */ + public static function xml_encoding($data, $registry) + { + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $encoding[] = 'UTF-16LE'; + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $encoding[] = 'UTF-8'; + } + // UTF-32 Big Endian Without BOM + elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") + { + if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian Without BOM + elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") + { + if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian Without BOM + elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") + { + if ($pos = strpos($data, "\x00\x3F\x00\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian Without BOM + elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") + { + if ($pos = strpos($data, "\x3F\x00\x3E\x00")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16LE'; + } + // US-ASCII (or superset) + elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") + { + if ($pos = strpos($data, "\x3F\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-8'; + } + // Fallback to UTF-8 + else + { + $encoding[] = 'UTF-8'; + } + return $encoding; + } + + public static function output_javascript() + { + if (function_exists('ob_gzhandler')) + { + ob_start('ob_gzhandler'); + } + header('Content-type: text/javascript; charset: UTF-8'); + header('Cache-Control: must-revalidate'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + ?> +function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { + if (placeholder != '') { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } + else { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } +} + +function embed_flash(bgcolor, width, height, link, loop, type) { + document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>'); +} + +function embed_flv(width, height, link, placeholder, loop, player) { + document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>'); +} + +function embed_wmedia(width, height, link) { + document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); +} + <?php + } + + /** + * Get the SimplePie build timestamp + * + * Uses the git index if it exists, otherwise uses the modification time + * of the newest file. + */ + public static function get_build() + { + $root = dirname(dirname(__FILE__)); + if (file_exists($root . '/.git/index')) + { + return filemtime($root . '/.git/index'); + } + elseif (file_exists($root . '/SimplePie')) + { + $time = 0; + foreach (glob($root . '/SimplePie/*.php') as $file) + { + if (($mtime = filemtime($file)) > $time) + { + $time = $mtime; + } + } + return $time; + } + elseif (file_exists(dirname(__FILE__) . '/Core.php')) + { + return filemtime(dirname(__FILE__) . '/Core.php'); + } + else + { + return filemtime(__FILE__); + } + } + + /** + * Format debugging information + */ + public static function debug(&$sp) + { + $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n"; + $info .= 'PHP ' . PHP_VERSION . "\n"; + if ($sp->error() !== null) + { + $info .= 'Error occurred: ' . $sp->error() . "\n"; + } + else + { + $info .= "No error found.\n"; + } + $info .= "Extensions:\n"; + $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'); + foreach ($extensions as $ext) + { + if (extension_loaded($ext)) + { + $info .= " $ext loaded\n"; + switch ($ext) + { + case 'pcre': + $info .= ' Version ' . PCRE_VERSION . "\n"; + break; + case 'curl': + $version = curl_version(); + $info .= ' Version ' . $version['version'] . "\n"; + break; + case 'mbstring': + $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n"; + break; + case 'iconv': + $info .= ' Version ' . ICONV_VERSION . "\n"; + break; + case 'xml': + $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n"; + break; + } + } + else + { + $info .= " $ext not loaded\n"; + } + } + return $info; + } + + public static function silence_errors($num, $str) + { + // No-op + } +} + +/** + * Class to validate and to work with IPv6 addresses. + * + * @package SimplePie + * @subpackage HTTP + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/package/Net_IPv6 + * @author Alexander Merz <alexander.merz@web.de> + * @author elfrink at introweb dot nl + * @author Josh Peck <jmp at joshpeck dot org> + * @author Geoffrey Sneddon <geoffers@gmail.com> + */ +class SimplePie_Net_IPv6 +{ + /** + * Uncompresses an IPv6 address + * + * RFC 4291 allows you to compress concecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and expands the '::' to + * the required number of zero pieces. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @author Alexander Merz <alexander.merz@web.de> + * @author elfrink at introweb dot nl + * @author Josh Peck <jmp at joshpeck dot org> + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @param string $ip An IPv6 address + * @return string The uncompressed IPv6 address + */ + public static function uncompress($ip) + { + $c1 = -1; + $c2 = -1; + if (substr_count($ip, '::') === 1) + { + list($ip1, $ip2) = explode('::', $ip); + if ($ip1 === '') + { + $c1 = -1; + } + else + { + $c1 = substr_count($ip1, ':'); + } + if ($ip2 === '') + { + $c2 = -1; + } + else + { + $c2 = substr_count($ip2, ':'); + } + if (strpos($ip2, '.') !== false) + { + $c2++; + } + // :: + if ($c1 === -1 && $c2 === -1) + { + $ip = '0:0:0:0:0:0:0:0'; + } + // ::xxx + else if ($c1 === -1) + { + $fill = str_repeat('0:', 7 - $c2); + $ip = str_replace('::', $fill, $ip); + } + // xxx:: + else if ($c2 === -1) + { + $fill = str_repeat(':0', 7 - $c1); + $ip = str_replace('::', $fill, $ip); + } + // xxx::xxx + else + { + $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); + $ip = str_replace('::', $fill, $ip); + } + } + return $ip; + } + + /** + * Compresses an IPv6 address + * + * RFC 4291 allows you to compress concecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and compresses consecutive + * zero pieces to '::'. + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * @see uncompress() + * @param string $ip An IPv6 address + * @return string The compressed IPv6 address + */ + public static function compress($ip) + { + // Prepare the IP to be compressed + $ip = self::uncompress($ip); + $ip_parts = self::split_v6_v4($ip); + + // Replace all leading zeros + $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); + + // Find bunches of zeros + if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) + { + $max = 0; + $pos = null; + foreach ($matches[0] as $match) + { + if (strlen($match[0]) > $max) + { + $max = strlen($match[0]); + $pos = $match[1]; + } + } + + $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); + } + + if ($ip_parts[1] !== '') + { + return implode(':', $ip_parts); + } + else + { + return $ip_parts[0]; + } + } + + /** + * Splits an IPv6 address into the IPv6 and IPv4 representation parts + * + * RFC 4291 allows you to represent the last two parts of an IPv6 address + * using the standard IPv4 representation + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param string $ip An IPv6 address + * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part + */ + private static function split_v6_v4($ip) + { + if (strpos($ip, '.') !== false) + { + $pos = strrpos($ip, ':'); + $ipv6_part = substr($ip, 0, $pos); + $ipv4_part = substr($ip, $pos + 1); + return array($ipv6_part, $ipv4_part); + } + else + { + return array($ip, ''); + } + } + + /** + * Checks an IPv6 address + * + * Checks if the given IP is a valid IPv6 address + * + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function check_ipv6($ip) + { + $ip = self::uncompress($ip); + list($ipv6, $ipv4) = self::split_v6_v4($ip); + $ipv6 = explode(':', $ipv6); + $ipv4 = explode('.', $ipv4); + if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) + { + foreach ($ipv6 as $ipv6_part) + { + // The section can't be empty + if ($ipv6_part === '') + return false; + + // Nor can it be over four characters + if (strlen($ipv6_part) > 4) + return false; + + // Remove leading zeros (this is safe because of the above) + $ipv6_part = ltrim($ipv6_part, '0'); + if ($ipv6_part === '') + $ipv6_part = '0'; + + // Check the value is valid + $value = hexdec($ipv6_part); + if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) + return false; + } + if (count($ipv4) === 4) + { + foreach ($ipv4 as $ipv4_part) + { + $value = (int) $ipv4_part; + if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) + return false; + } + } + return true; + } + else + { + return false; + } + } + + /** + * Checks if the given IP is a valid IPv6 address + * + * @codeCoverageIgnore + * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead + * @see check_ipv6 + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function checkIPv6($ip) + { + return self::check_ipv6($ip); + } +} + +/** + * Date Parser + * + * @package SimplePie + * @subpackage Parsing + */ +class SimplePie_Parse_Date +{ + /** + * Input data + * + * @access protected + * @var string + */ + var $date; + + /** + * List of days, calendar day name => ordinal day number in the week + * + * @access protected + * @var array + */ + var $day = array( + // English + 'mon' => 1, + 'monday' => 1, + 'tue' => 2, + 'tuesday' => 2, + 'wed' => 3, + 'wednesday' => 3, + 'thu' => 4, + 'thursday' => 4, + 'fri' => 5, + 'friday' => 5, + 'sat' => 6, + 'saturday' => 6, + 'sun' => 7, + 'sunday' => 7, + // Dutch + 'maandag' => 1, + 'dinsdag' => 2, + 'woensdag' => 3, + 'donderdag' => 4, + 'vrijdag' => 5, + 'zaterdag' => 6, + 'zondag' => 7, + // French + 'lundi' => 1, + 'mardi' => 2, + 'mercredi' => 3, + 'jeudi' => 4, + 'vendredi' => 5, + 'samedi' => 6, + 'dimanche' => 7, + // German + 'montag' => 1, + 'dienstag' => 2, + 'mittwoch' => 3, + 'donnerstag' => 4, + 'freitag' => 5, + 'samstag' => 6, + 'sonnabend' => 6, + 'sonntag' => 7, + // Italian + 'lunedì' => 1, + 'martedì' => 2, + 'mercoledì' => 3, + 'giovedì' => 4, + 'venerdì' => 5, + 'sabato' => 6, + 'domenica' => 7, + // Spanish + 'lunes' => 1, + 'martes' => 2, + 'miércoles' => 3, + 'jueves' => 4, + 'viernes' => 5, + 'sábado' => 6, + 'domingo' => 7, + // Finnish + 'maanantai' => 1, + 'tiistai' => 2, + 'keskiviikko' => 3, + 'torstai' => 4, + 'perjantai' => 5, + 'lauantai' => 6, + 'sunnuntai' => 7, + // Hungarian + 'hétfő' => 1, + 'kedd' => 2, + 'szerda' => 3, + 'csütörtok' => 4, + 'péntek' => 5, + 'szombat' => 6, + 'vasárnap' => 7, + // Greek + 'Δευ' => 1, + 'Τρι' => 2, + 'Τετ' => 3, + 'Πεμ' => 4, + 'Παρ' => 5, + 'Σαβ' => 6, + 'Κυρ' => 7, + ); + + /** + * List of months, calendar month name => calendar month number + * + * @access protected + * @var array + */ + var $month = array( + // English + 'jan' => 1, + 'january' => 1, + 'feb' => 2, + 'february' => 2, + 'mar' => 3, + 'march' => 3, + 'apr' => 4, + 'april' => 4, + 'may' => 5, + // No long form of May + 'jun' => 6, + 'june' => 6, + 'jul' => 7, + 'july' => 7, + 'aug' => 8, + 'august' => 8, + 'sep' => 9, + 'september' => 8, + 'oct' => 10, + 'october' => 10, + 'nov' => 11, + 'november' => 11, + 'dec' => 12, + 'december' => 12, + // Dutch + 'januari' => 1, + 'februari' => 2, + 'maart' => 3, + 'april' => 4, + 'mei' => 5, + 'juni' => 6, + 'juli' => 7, + 'augustus' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'december' => 12, + // French + 'janvier' => 1, + 'février' => 2, + 'mars' => 3, + 'avril' => 4, + 'mai' => 5, + 'juin' => 6, + 'juillet' => 7, + 'août' => 8, + 'septembre' => 9, + 'octobre' => 10, + 'novembre' => 11, + 'décembre' => 12, + // German + 'januar' => 1, + 'februar' => 2, + 'märz' => 3, + 'april' => 4, + 'mai' => 5, + 'juni' => 6, + 'juli' => 7, + 'august' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'dezember' => 12, + // Italian + 'gennaio' => 1, + 'febbraio' => 2, + 'marzo' => 3, + 'aprile' => 4, + 'maggio' => 5, + 'giugno' => 6, + 'luglio' => 7, + 'agosto' => 8, + 'settembre' => 9, + 'ottobre' => 10, + 'novembre' => 11, + 'dicembre' => 12, + // Spanish + 'enero' => 1, + 'febrero' => 2, + 'marzo' => 3, + 'abril' => 4, + 'mayo' => 5, + 'junio' => 6, + 'julio' => 7, + 'agosto' => 8, + 'septiembre' => 9, + 'setiembre' => 9, + 'octubre' => 10, + 'noviembre' => 11, + 'diciembre' => 12, + // Finnish + 'tammikuu' => 1, + 'helmikuu' => 2, + 'maaliskuu' => 3, + 'huhtikuu' => 4, + 'toukokuu' => 5, + 'kesäkuu' => 6, + 'heinäkuu' => 7, + 'elokuu' => 8, + 'suuskuu' => 9, + 'lokakuu' => 10, + 'marras' => 11, + 'joulukuu' => 12, + // Hungarian + 'január' => 1, + 'február' => 2, + 'március' => 3, + 'április' => 4, + 'május' => 5, + 'június' => 6, + 'július' => 7, + 'augusztus' => 8, + 'szeptember' => 9, + 'október' => 10, + 'november' => 11, + 'december' => 12, + // Greek + 'Ιαν' => 1, + 'Φεβ' => 2, + 'Μάώ' => 3, + 'Μαώ' => 3, + 'Απρ' => 4, + 'Μάι' => 5, + 'Μαϊ' => 5, + 'Μαι' => 5, + 'Ιούν' => 6, + 'Ιον' => 6, + 'Ιούλ' => 7, + 'Ιολ' => 7, + 'Αύγ' => 8, + 'Αυγ' => 8, + 'Σεπ' => 9, + 'Οκτ' => 10, + 'Νοέ' => 11, + 'Δεκ' => 12, + ); + + /** + * List of timezones, abbreviation => offset from UTC + * + * @access protected + * @var array + */ + var $timezone = array( + 'ACDT' => 37800, + 'ACIT' => 28800, + 'ACST' => 34200, + 'ACT' => -18000, + 'ACWDT' => 35100, + 'ACWST' => 31500, + 'AEDT' => 39600, + 'AEST' => 36000, + 'AFT' => 16200, + 'AKDT' => -28800, + 'AKST' => -32400, + 'AMDT' => 18000, + 'AMT' => -14400, + 'ANAST' => 46800, + 'ANAT' => 43200, + 'ART' => -10800, + 'AZOST' => -3600, + 'AZST' => 18000, + 'AZT' => 14400, + 'BIOT' => 21600, + 'BIT' => -43200, + 'BOT' => -14400, + 'BRST' => -7200, + 'BRT' => -10800, + 'BST' => 3600, + 'BTT' => 21600, + 'CAST' => 18000, + 'CAT' => 7200, + 'CCT' => 23400, + 'CDT' => -18000, + 'CEDT' => 7200, + 'CET' => 3600, + 'CGST' => -7200, + 'CGT' => -10800, + 'CHADT' => 49500, + 'CHAST' => 45900, + 'CIST' => -28800, + 'CKT' => -36000, + 'CLDT' => -10800, + 'CLST' => -14400, + 'COT' => -18000, + 'CST' => -21600, + 'CVT' => -3600, + 'CXT' => 25200, + 'DAVT' => 25200, + 'DTAT' => 36000, + 'EADT' => -18000, + 'EAST' => -21600, + 'EAT' => 10800, + 'ECT' => -18000, + 'EDT' => -14400, + 'EEST' => 10800, + 'EET' => 7200, + 'EGT' => -3600, + 'EKST' => 21600, + 'EST' => -18000, + 'FJT' => 43200, + 'FKDT' => -10800, + 'FKST' => -14400, + 'FNT' => -7200, + 'GALT' => -21600, + 'GEDT' => 14400, + 'GEST' => 10800, + 'GFT' => -10800, + 'GILT' => 43200, + 'GIT' => -32400, + 'GST' => 14400, + 'GST' => -7200, + 'GYT' => -14400, + 'HAA' => -10800, + 'HAC' => -18000, + 'HADT' => -32400, + 'HAE' => -14400, + 'HAP' => -25200, + 'HAR' => -21600, + 'HAST' => -36000, + 'HAT' => -9000, + 'HAY' => -28800, + 'HKST' => 28800, + 'HMT' => 18000, + 'HNA' => -14400, + 'HNC' => -21600, + 'HNE' => -18000, + 'HNP' => -28800, + 'HNR' => -25200, + 'HNT' => -12600, + 'HNY' => -32400, + 'IRDT' => 16200, + 'IRKST' => 32400, + 'IRKT' => 28800, + 'IRST' => 12600, + 'JFDT' => -10800, + 'JFST' => -14400, + 'JST' => 32400, + 'KGST' => 21600, + 'KGT' => 18000, + 'KOST' => 39600, + 'KOVST' => 28800, + 'KOVT' => 25200, + 'KRAST' => 28800, + 'KRAT' => 25200, + 'KST' => 32400, + 'LHDT' => 39600, + 'LHST' => 37800, + 'LINT' => 50400, + 'LKT' => 21600, + 'MAGST' => 43200, + 'MAGT' => 39600, + 'MAWT' => 21600, + 'MDT' => -21600, + 'MESZ' => 7200, + 'MEZ' => 3600, + 'MHT' => 43200, + 'MIT' => -34200, + 'MNST' => 32400, + 'MSDT' => 14400, + 'MSST' => 10800, + 'MST' => -25200, + 'MUT' => 14400, + 'MVT' => 18000, + 'MYT' => 28800, + 'NCT' => 39600, + 'NDT' => -9000, + 'NFT' => 41400, + 'NMIT' => 36000, + 'NOVST' => 25200, + 'NOVT' => 21600, + 'NPT' => 20700, + 'NRT' => 43200, + 'NST' => -12600, + 'NUT' => -39600, + 'NZDT' => 46800, + 'NZST' => 43200, + 'OMSST' => 25200, + 'OMST' => 21600, + 'PDT' => -25200, + 'PET' => -18000, + 'PETST' => 46800, + 'PETT' => 43200, + 'PGT' => 36000, + 'PHOT' => 46800, + 'PHT' => 28800, + 'PKT' => 18000, + 'PMDT' => -7200, + 'PMST' => -10800, + 'PONT' => 39600, + 'PST' => -28800, + 'PWT' => 32400, + 'PYST' => -10800, + 'PYT' => -14400, + 'RET' => 14400, + 'ROTT' => -10800, + 'SAMST' => 18000, + 'SAMT' => 14400, + 'SAST' => 7200, + 'SBT' => 39600, + 'SCDT' => 46800, + 'SCST' => 43200, + 'SCT' => 14400, + 'SEST' => 3600, + 'SGT' => 28800, + 'SIT' => 28800, + 'SRT' => -10800, + 'SST' => -39600, + 'SYST' => 10800, + 'SYT' => 7200, + 'TFT' => 18000, + 'THAT' => -36000, + 'TJT' => 18000, + 'TKT' => -36000, + 'TMT' => 18000, + 'TOT' => 46800, + 'TPT' => 32400, + 'TRUT' => 36000, + 'TVT' => 43200, + 'TWT' => 28800, + 'UYST' => -7200, + 'UYT' => -10800, + 'UZT' => 18000, + 'VET' => -14400, + 'VLAST' => 39600, + 'VLAT' => 36000, + 'VOST' => 21600, + 'VUT' => 39600, + 'WAST' => 7200, + 'WAT' => 3600, + 'WDT' => 32400, + 'WEST' => 3600, + 'WFT' => 43200, + 'WIB' => 25200, + 'WIT' => 32400, + 'WITA' => 28800, + 'WKST' => 18000, + 'WST' => 28800, + 'YAKST' => 36000, + 'YAKT' => 32400, + 'YAPT' => 36000, + 'YEKST' => 21600, + 'YEKT' => 18000, + ); + + /** + * Cached PCRE for SimplePie_Parse_Date::$day * - * @access public - * @param string $path - * @return bool + * @access protected + * @var string */ - function set_path($path) - { - if ($path === null || $path === '') - { - $this->path = null; - $this->valid[__FUNCTION__] = true; - return true; - } - elseif (substr($path, 0, 2) === '//' && $this->userinfo === null && $this->host === null && $this->port === null) - { - $this->path = null; - $this->valid[__FUNCTION__] = false; - return false; - } - else - { - $this->path = $this->replace_invalid_with_pct_encoding($path, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=@/'); - if ($this->scheme !== null) - { - $this->path = $this->remove_dot_segments($this->path); - } - $this->valid[__FUNCTION__] = true; - return true; - } - } + var $day_pcre; /** - * Set the query. + * Cached PCRE for SimplePie_Parse_Date::$month * - * @access public - * @param string $query - * @return bool + * @access protected + * @var string */ - function set_query($query) - { - if ($query === null || $query === '') - { - $this->query = null; - } - else - { - $this->query = $this->replace_invalid_with_pct_encoding($query, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$\'()*+,;:@/?&='); - } - $this->valid[__FUNCTION__] = true; - return true; - } + var $month_pcre; /** - * Set the fragment. + * Array of user-added callback methods * - * @access public - * @param string $fragment - * @return bool + * @access private + * @var array */ - function set_fragment($fragment) - { - if ($fragment === null || $fragment === '') - { - $this->fragment = null; - } - else - { - $this->fragment = $this->replace_invalid_with_pct_encoding($fragment, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:@/?'); - } - $this->valid[__FUNCTION__] = true; - return true; - } + var $built_in = array(); /** - * Get the complete IRI + * Array of user-added callback methods * - * @access public - * @return string + * @access private + * @var array */ - function get_iri() - { - $iri = ''; - if ($this->scheme !== null) - { - $iri .= $this->scheme . ':'; - } - if (($authority = $this->get_authority()) !== null) - { - $iri .= '//' . $authority; - } - if ($this->path !== null) - { - $iri .= $this->path; - } - if ($this->query !== null) - { - $iri .= '?' . $this->query; - } - if ($this->fragment !== null) - { - $iri .= '#' . $this->fragment; - } - - if ($iri !== '') - { - return $iri; - } - else - { - return null; - } - } + var $user = array(); /** - * Get the scheme + * Create new SimplePie_Parse_Date object, and set self::day_pcre, + * self::month_pcre, and self::built_in * - * @access public - * @return string + * @access private */ - function get_scheme() + public function __construct() { - return $this->scheme; - } + $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; + $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; - /** - * Get the complete authority - * - * @access public - * @return string - */ - function get_authority() - { - $authority = ''; - if ($this->userinfo !== null) - { - $authority .= $this->userinfo . '@'; - } - if ($this->host !== null) - { - $authority .= $this->host; - } - if ($this->port !== null) + static $cache; + if (!isset($cache[get_class($this)])) { - $authority .= ':' . $this->port; - } + $all_methods = get_class_methods($this); - if ($authority !== '') - { - return $authority; + foreach ($all_methods as $method) + { + if (strtolower(substr($method, 0, 5)) === 'date_') + { + $cache[get_class($this)][] = $method; + } + } } - else + + foreach ($cache[get_class($this)] as $method) { - return null; + $this->built_in[] = $method; } } /** - * Get the user information - * - * @access public - * @return string - */ - function get_userinfo() - { - return $this->userinfo; - } - - /** - * Get the host - * - * @access public - * @return string - */ - function get_host() - { - return $this->host; - } - - /** - * Get the port - * - * @access public - * @return string - */ - function get_port() - { - return $this->port; - } - - /** - * Get the path - * - * @access public - * @return string - */ - function get_path() - { - return $this->path; - } - - /** - * Get the query - * - * @access public - * @return string - */ - function get_query() - { - return $this->query; - } - - /** - * Get the fragment - * - * @access public - * @return string - */ - function get_fragment() - { - return $this->fragment; - } -} - -/** - * Class to validate and to work with IPv6 addresses. - * - * @package SimplePie - * @copyright 2003-2005 The PHP Group - * @license http://www.opensource.org/licenses/bsd-license.php - * @link http://pear.php.net/package/Net_IPv6 - * @author Alexander Merz <alexander.merz@web.de> - * @author elfrink at introweb dot nl - * @author Josh Peck <jmp at joshpeck dot org> - * @author Geoffrey Sneddon <geoffers@gmail.com> - */ -class SimplePie_Net_IPv6 -{ - /** - * Removes a possible existing netmask specification of an IP address. + * Get the object * - * @param string $ip the (compressed) IP as Hex representation - * @return string the IP the without netmask - * @since 1.1.0 * @access public - * @static */ - function removeNetmaskSpec($ip) + public static function get() { - if (strpos($ip, '/') !== false) - { - list($addr, $nm) = explode('/', $ip); - } - else + static $object; + if (!$object) { - $addr = $ip; + $object = new SimplePie_Parse_Date; } - return $addr; + return $object; } /** - * Uncompresses an IPv6 address - * - * RFC 2373 allows you to compress zeros in an address to '::'. This - * function expects an valid IPv6 address and expands the '::' to - * the required zeros. - * - * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 - * ::1 -> 0:0:0:0:0:0:0:1 - * - * @access public - * @static - * @param string $ip a valid IPv6-address (hex format) - * @return string the uncompressed IPv6-address (hex format) - */ - function Uncompress($ip) - { - $uip = SimplePie_Net_IPv6::removeNetmaskSpec($ip); - $c1 = -1; - $c2 = -1; - if (strpos($ip, '::') !== false) - { - list($ip1, $ip2) = explode('::', $ip); - if ($ip1 === '') - { - $c1 = -1; - } - else - { - $pos = 0; - if (($pos = substr_count($ip1, ':')) > 0) - { - $c1 = $pos; - } - else - { - $c1 = 0; - } - } - if ($ip2 === '') - { - $c2 = -1; - } - else - { - $pos = 0; - if (($pos = substr_count($ip2, ':')) > 0) - { - $c2 = $pos; - } - else - { - $c2 = 0; - } - } - if (strstr($ip2, '.')) - { - $c2++; - } - // :: - if ($c1 === -1 && $c2 === -1) - { - $uip = '0:0:0:0:0:0:0:0'; - } - // ::xxx - else if ($c1 === -1) - { - $fill = str_repeat('0:', 7 - $c2); - $uip = str_replace('::', $fill, $uip); - } - // xxx:: - else if ($c2 === -1) + * Parse a date + * + * @final + * @access public + * @param string $date Date to parse + * @return int Timestamp corresponding to date string, or false on failure + */ + public function parse($date) + { + foreach ($this->user as $method) + { + if (($returned = call_user_func($method, $date)) !== false) { - $fill = str_repeat(':0', 7 - $c1); - $uip = str_replace('::', $fill, $uip); + return $returned; } - // xxx::xxx - else + } + + foreach ($this->built_in as $method) + { + if (($returned = call_user_func(array($this, $method), $date)) !== false) { - $fill = str_repeat(':0:', 6 - $c2 - $c1); - $uip = str_replace('::', $fill, $uip); - $uip = str_replace('::', ':', $uip); + return $returned; } } - return $uip; + + return false; } /** - * Splits an IPv6 address into the IPv6 and a possible IPv4 part - * - * RFC 2373 allows you to note the last two parts of an IPv6 address as - * an IPv4 compatible address - * - * Example: 0:0:0:0:0:0:13.1.68.3 - * 0:0:0:0:0:FFFF:129.144.52.38 + * Add a callback method to parse a date * + * @final * @access public - * @static - * @param string $ip a valid IPv6-address (hex format) - * @return array [0] contains the IPv6 part, [1] the IPv4 part (hex format) + * @param callback $callback */ - function SplitV64($ip) + public function add_callback($callback) { - $ip = SimplePie_Net_IPv6::Uncompress($ip); - if (strstr($ip, '.')) + if (is_callable($callback)) { - $pos = strrpos($ip, ':'); - $ip[$pos] = '_'; - $ipPart = explode('_', $ip); - return $ipPart; + $this->user[] = $callback; } else { - return array($ip, ''); + trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); } } /** - * Checks an IPv6 address - * - * Checks if the given IP is IPv6-compatible + * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as + * well as allowing any of upper or lower case "T", horizontal tabs, or + * spaces to be used as the time seperator (including more than one)) * - * @access public - * @static - * @param string $ip a valid IPv6-address - * @return bool true if $ip is an IPv6 address + * @access protected + * @return int Timestamp */ - function checkIPv6($ip) + public function date_w3cdtf($date) { - $ipPart = SimplePie_Net_IPv6::SplitV64($ip); - $count = 0; - if (!empty($ipPart[0])) + static $pcre; + if (!$pcre) + { + $year = '([0-9]{4})'; + $month = $day = $hour = $minute = $second = '([0-9]{2})'; + $decimal = '([0-9]*)'; + $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; + $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; + } + if (preg_match($pcre, $date, $match)) { - $ipv6 = explode(':', $ipPart[0]); - for ($i = 0; $i < count($ipv6); $i++) + /* + Capturing subpatterns: + 1: Year + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Decimal fraction of a second + 8: Zulu + 9: Timezone ± + 10: Timezone hours + 11: Timezone minutes + */ + + // Fill in empty matches + for ($i = count($match); $i <= 3; $i++) { - $dec = hexdec($ipv6[$i]); - $hex = strtoupper(preg_replace('/^[0]{1,3}(.*[0-9a-fA-F])$/', '\\1', $ipv6[$i])); - if ($ipv6[$i] >= 0 && $dec <= 65535 && $hex === strtoupper(dechex($dec))) - { - $count++; - } + $match[$i] = '1'; } - if ($count === 8) + + for ($i = count($match); $i <= 7; $i++) { - return true; + $match[$i] = '0'; } - elseif ($count === 6 && !empty($ipPart[1])) + + // Numeric timezone + if (isset($match[9]) && $match[9] !== '') { - $ipv4 = explode('.', $ipPart[1]); - $count = 0; - foreach ($ipv4 as $ipv4_part) - { - if ($ipv4_part >= 0 && $ipv4_part <= 255 && preg_match('/^\d{1,3}$/', $ipv4_part)) - { - $count++; - } - } - if ($count === 4) + $timezone = $match[10] * 3600; + $timezone += $match[11] * 60; + if ($match[9] === '-') { - return true; + $timezone = 0 - $timezone; } } else { - return false; + $timezone = 0; } + // Convert the number of seconds to an integer, taking decimals into account + $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); + + return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; } else { return false; } } -} -/** - * Date Parser - * - * @package SimplePie - */ -class SimplePie_Parse_Date -{ /** - * Input data + * Remove RFC822 comments * * @access protected - * @var string + * @param string $data Data to strip comments from + * @return string Comment stripped string */ - var $date; + public function remove_rfc2822_comments($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } /** - * List of days, calendar day name => ordinal day number in the week + * Parse RFC2822's date format * * @access protected - * @var array + * @return int Timestamp */ - var $day = array( - // English - 'mon' => 1, - 'monday' => 1, - 'tue' => 2, - 'tuesday' => 2, - 'wed' => 3, - 'wednesday' => 3, - 'thu' => 4, - 'thursday' => 4, - 'fri' => 5, - 'friday' => 5, - 'sat' => 6, - 'saturday' => 6, - 'sun' => 7, - 'sunday' => 7, - // Dutch - 'maandag' => 1, - 'dinsdag' => 2, - 'woensdag' => 3, - 'donderdag' => 4, - 'vrijdag' => 5, - 'zaterdag' => 6, - 'zondag' => 7, - // French - 'lundi' => 1, - 'mardi' => 2, - 'mercredi' => 3, - 'jeudi' => 4, - 'vendredi' => 5, - 'samedi' => 6, - 'dimanche' => 7, - // German - 'montag' => 1, - 'dienstag' => 2, - 'mittwoch' => 3, - 'donnerstag' => 4, - 'freitag' => 5, - 'samstag' => 6, - 'sonnabend' => 6, - 'sonntag' => 7, - // Italian - 'lunedì' => 1, - 'martedì' => 2, - 'mercoledì' => 3, - 'giovedì' => 4, - 'venerdì' => 5, - 'sabato' => 6, - 'domenica' => 7, - // Spanish - 'lunes' => 1, - 'martes' => 2, - 'miércoles' => 3, - 'jueves' => 4, - 'viernes' => 5, - 'sábado' => 6, - 'domingo' => 7, - // Finnish - 'maanantai' => 1, - 'tiistai' => 2, - 'keskiviikko' => 3, - 'torstai' => 4, - 'perjantai' => 5, - 'lauantai' => 6, - 'sunnuntai' => 7, - // Hungarian - 'hétfő' => 1, - 'kedd' => 2, - 'szerda' => 3, - 'csütörtok' => 4, - 'péntek' => 5, - 'szombat' => 6, - 'vasárnap' => 7, - // Greek - 'Δευ' => 1, - 'Τρι' => 2, - 'Τετ' => 3, - 'Πεμ' => 4, - 'Παρ' => 5, - 'Σαβ' => 6, - 'Κυρ' => 7, - ); + public function date_rfc2822($date) + { + static $pcre; + if (!$pcre) + { + $wsp = '[\x09\x20]'; + $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; + $optional_fws = $fws . '?'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $minute = $second = '([0-9]{2})'; + $year = '([0-9]{2,4})'; + $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; + $character_zone = '([A-Z]{1,5})'; + $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; + $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; + } + if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone ± + 9: Timezone hours + 10: Timezone minutes + 11: Alphabetic timezone + */ + + // Find the month number + $month = $this->month[strtolower($match[3])]; + + // Numeric timezone + if ($match[8] !== '') + { + $timezone = $match[9] * 3600; + $timezone += $match[10] * 60; + if ($match[8] === '-') + { + $timezone = 0 - $timezone; + } + } + // Character timezone + elseif (isset($this->timezone[strtoupper($match[11])])) + { + $timezone = $this->timezone[strtoupper($match[11])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2/3 digit years + if ($match[4] < 50) + { + $match[4] += 2000; + } + elseif ($match[4] < 1000) + { + $match[4] += 1900; + } + + // Second is optional, if it is empty set it to zero + if ($match[7] !== '') + { + $second = $match[7]; + } + else + { + $second = 0; + } + + return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } /** - * List of months, calendar month name => calendar month number + * Parse RFC850's date format * * @access protected - * @var array + * @return int Timestamp */ - var $month = array( - // English - 'jan' => 1, - 'january' => 1, - 'feb' => 2, - 'february' => 2, - 'mar' => 3, - 'march' => 3, - 'apr' => 4, - 'april' => 4, - 'may' => 5, - // No long form of May - 'jun' => 6, - 'june' => 6, - 'jul' => 7, - 'july' => 7, - 'aug' => 8, - 'august' => 8, - 'sep' => 9, - 'september' => 8, - 'oct' => 10, - 'october' => 10, - 'nov' => 11, - 'november' => 11, - 'dec' => 12, - 'december' => 12, - // Dutch - 'januari' => 1, - 'februari' => 2, - 'maart' => 3, - 'april' => 4, - 'mei' => 5, - 'juni' => 6, - 'juli' => 7, - 'augustus' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'december' => 12, - // French - 'janvier' => 1, - 'février' => 2, - 'mars' => 3, - 'avril' => 4, - 'mai' => 5, - 'juin' => 6, - 'juillet' => 7, - 'août' => 8, - 'septembre' => 9, - 'octobre' => 10, - 'novembre' => 11, - 'décembre' => 12, - // German - 'januar' => 1, - 'februar' => 2, - 'märz' => 3, - 'april' => 4, - 'mai' => 5, - 'juni' => 6, - 'juli' => 7, - 'august' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'dezember' => 12, - // Italian - 'gennaio' => 1, - 'febbraio' => 2, - 'marzo' => 3, - 'aprile' => 4, - 'maggio' => 5, - 'giugno' => 6, - 'luglio' => 7, - 'agosto' => 8, - 'settembre' => 9, - 'ottobre' => 10, - 'novembre' => 11, - 'dicembre' => 12, - // Spanish - 'enero' => 1, - 'febrero' => 2, - 'marzo' => 3, - 'abril' => 4, - 'mayo' => 5, - 'junio' => 6, - 'julio' => 7, - 'agosto' => 8, - 'septiembre' => 9, - 'setiembre' => 9, - 'octubre' => 10, - 'noviembre' => 11, - 'diciembre' => 12, - // Finnish - 'tammikuu' => 1, - 'helmikuu' => 2, - 'maaliskuu' => 3, - 'huhtikuu' => 4, - 'toukokuu' => 5, - 'kesäkuu' => 6, - 'heinäkuu' => 7, - 'elokuu' => 8, - 'suuskuu' => 9, - 'lokakuu' => 10, - 'marras' => 11, - 'joulukuu' => 12, - // Hungarian - 'január' => 1, - 'február' => 2, - 'március' => 3, - 'április' => 4, - 'május' => 5, - 'június' => 6, - 'július' => 7, - 'augusztus' => 8, - 'szeptember' => 9, - 'október' => 10, - 'november' => 11, - 'december' => 12, - // Greek - 'Ιαν' => 1, - 'Φεβ' => 2, - 'Μάώ' => 3, - 'Μαώ' => 3, - 'Απρ' => 4, - 'Μάι' => 5, - 'Μαϊ' => 5, - 'Μαι' => 5, - 'Ιούν' => 6, - 'Ιον' => 6, - 'Ιούλ' => 7, - 'Ιολ' => 7, - 'Αύγ' => 8, - 'Αυγ' => 8, - 'Σεπ' => 9, - 'Οκτ' => 10, - 'Νοέ' => 11, - 'Δεκ' => 12, - ); + public function date_rfc850($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $year = $hour = $minute = $second = '([0-9]{2})'; + $zone = '([A-Z]{1,5})'; + $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone + */ + + // Month + $month = $this->month[strtolower($match[3])]; + + // Character timezone + if (isset($this->timezone[strtoupper($match[8])])) + { + $timezone = $this->timezone[strtoupper($match[8])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } - /** - * List of timezones, abbreviation => offset from UTC - * - * @access protected - * @var array - */ - var $timezone = array( - 'ACDT' => 37800, - 'ACIT' => 28800, - 'ACST' => 34200, - 'ACT' => -18000, - 'ACWDT' => 35100, - 'ACWST' => 31500, - 'AEDT' => 39600, - 'AEST' => 36000, - 'AFT' => 16200, - 'AKDT' => -28800, - 'AKST' => -32400, - 'AMDT' => 18000, - 'AMT' => -14400, - 'ANAST' => 46800, - 'ANAT' => 43200, - 'ART' => -10800, - 'AZOST' => -3600, - 'AZST' => 18000, - 'AZT' => 14400, - 'BIOT' => 21600, - 'BIT' => -43200, - 'BOT' => -14400, - 'BRST' => -7200, - 'BRT' => -10800, - 'BST' => 3600, - 'BTT' => 21600, - 'CAST' => 18000, - 'CAT' => 7200, - 'CCT' => 23400, - 'CDT' => -18000, - 'CEDT' => 7200, - 'CET' => 3600, - 'CGST' => -7200, - 'CGT' => -10800, - 'CHADT' => 49500, - 'CHAST' => 45900, - 'CIST' => -28800, - 'CKT' => -36000, - 'CLDT' => -10800, - 'CLST' => -14400, - 'COT' => -18000, - 'CST' => -21600, - 'CVT' => -3600, - 'CXT' => 25200, - 'DAVT' => 25200, - 'DTAT' => 36000, - 'EADT' => -18000, - 'EAST' => -21600, - 'EAT' => 10800, - 'ECT' => -18000, - 'EDT' => -14400, - 'EEST' => 10800, - 'EET' => 7200, - 'EGT' => -3600, - 'EKST' => 21600, - 'EST' => -18000, - 'FJT' => 43200, - 'FKDT' => -10800, - 'FKST' => -14400, - 'FNT' => -7200, - 'GALT' => -21600, - 'GEDT' => 14400, - 'GEST' => 10800, - 'GFT' => -10800, - 'GILT' => 43200, - 'GIT' => -32400, - 'GST' => 14400, - 'GST' => -7200, - 'GYT' => -14400, - 'HAA' => -10800, - 'HAC' => -18000, - 'HADT' => -32400, - 'HAE' => -14400, - 'HAP' => -25200, - 'HAR' => -21600, - 'HAST' => -36000, - 'HAT' => -9000, - 'HAY' => -28800, - 'HKST' => 28800, - 'HMT' => 18000, - 'HNA' => -14400, - 'HNC' => -21600, - 'HNE' => -18000, - 'HNP' => -28800, - 'HNR' => -25200, - 'HNT' => -12600, - 'HNY' => -32400, - 'IRDT' => 16200, - 'IRKST' => 32400, - 'IRKT' => 28800, - 'IRST' => 12600, - 'JFDT' => -10800, - 'JFST' => -14400, - 'JST' => 32400, - 'KGST' => 21600, - 'KGT' => 18000, - 'KOST' => 39600, - 'KOVST' => 28800, - 'KOVT' => 25200, - 'KRAST' => 28800, - 'KRAT' => 25200, - 'KST' => 32400, - 'LHDT' => 39600, - 'LHST' => 37800, - 'LINT' => 50400, - 'LKT' => 21600, - 'MAGST' => 43200, - 'MAGT' => 39600, - 'MAWT' => 21600, - 'MDT' => -21600, - 'MESZ' => 7200, - 'MEZ' => 3600, - 'MHT' => 43200, - 'MIT' => -34200, - 'MNST' => 32400, - 'MSDT' => 14400, - 'MSST' => 10800, - 'MST' => -25200, - 'MUT' => 14400, - 'MVT' => 18000, - 'MYT' => 28800, - 'NCT' => 39600, - 'NDT' => -9000, - 'NFT' => 41400, - 'NMIT' => 36000, - 'NOVST' => 25200, - 'NOVT' => 21600, - 'NPT' => 20700, - 'NRT' => 43200, - 'NST' => -12600, - 'NUT' => -39600, - 'NZDT' => 46800, - 'NZST' => 43200, - 'OMSST' => 25200, - 'OMST' => 21600, - 'PDT' => -25200, - 'PET' => -18000, - 'PETST' => 46800, - 'PETT' => 43200, - 'PGT' => 36000, - 'PHOT' => 46800, - 'PHT' => 28800, - 'PKT' => 18000, - 'PMDT' => -7200, - 'PMST' => -10800, - 'PONT' => 39600, - 'PST' => -28800, - 'PWT' => 32400, - 'PYST' => -10800, - 'PYT' => -14400, - 'RET' => 14400, - 'ROTT' => -10800, - 'SAMST' => 18000, - 'SAMT' => 14400, - 'SAST' => 7200, - 'SBT' => 39600, - 'SCDT' => 46800, - 'SCST' => 43200, - 'SCT' => 14400, - 'SEST' => 3600, - 'SGT' => 28800, - 'SIT' => 28800, - 'SRT' => -10800, - 'SST' => -39600, - 'SYST' => 10800, - 'SYT' => 7200, - 'TFT' => 18000, - 'THAT' => -36000, - 'TJT' => 18000, - 'TKT' => -36000, - 'TMT' => 18000, - 'TOT' => 46800, - 'TPT' => 32400, - 'TRUT' => 36000, - 'TVT' => 43200, - 'TWT' => 28800, - 'UYST' => -7200, - 'UYT' => -10800, - 'UZT' => 18000, - 'VET' => -14400, - 'VLAST' => 39600, - 'VLAT' => 36000, - 'VOST' => 21600, - 'VUT' => 39600, - 'WAST' => 7200, - 'WAT' => 3600, - 'WDT' => 32400, - 'WEST' => 3600, - 'WFT' => 43200, - 'WIB' => 25200, - 'WIT' => 32400, - 'WITA' => 28800, - 'WKST' => 18000, - 'WST' => 28800, - 'YAKST' => 36000, - 'YAKT' => 32400, - 'YAPT' => 36000, - 'YEKST' => 21600, - 'YEKT' => 18000, - ); + // Deal with 2 digit year + if ($match[4] < 50) + { + $match[4] += 2000; + } + else + { + $match[4] += 1900; + } + + return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } /** - * Cached PCRE for SimplePie_Parse_Date::$day + * Parse C99's asctime()'s date format * * @access protected - * @var string + * @return int Timestamp */ - var $day_pcre; + public function date_asctime($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $wday_name = $this->day_pcre; + $mon_name = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $sec = $min = '([0-9]{2})'; + $year = '([0-9]{4})'; + $terminator = '\x0A?\x00?'; + $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Year + */ + + $month = $this->month[strtolower($match[2])]; + return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); + } + else + { + return false; + } + } /** - * Cached PCRE for SimplePie_Parse_Date::$month + * Parse dates using strtotime() * * @access protected - * @var string + * @return int Timestamp */ - var $month_pcre; + public function date_strtotime($date) + { + $strtotime = strtotime($date); + if ($strtotime === -1 || $strtotime === false) + { + return false; + } + else + { + return $strtotime; + } + } +} - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $built_in = array(); +/** + * Parses XML into something sane + * + * + * This class can be overloaded with {@see SimplePie::set_parser_class()} + * + * @package SimplePie + * @subpackage Parsing + */ +class SimplePie_Parser +{ + var $error_code; + var $error_string; + var $current_line; + var $current_column; + var $current_byte; + var $separator = ' '; + var $namespace = array(''); + var $element = array(''); + var $xml_base = array(''); + var $xml_base_explicit = array(false); + var $xml_lang = array(''); + var $data = array(); + var $datas = array(array()); + var $current_xhtml_construct = -1; + var $encoding; + protected $registry; - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $user = array(); + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } - /** - * Create new SimplePie_Parse_Date object, and set self::day_pcre, - * self::month_pcre, and self::built_in - * - * @access private - */ - function SimplePie_Parse_Date() + public function parse(&$data, $encoding) { - $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; - $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character + if (strtoupper($encoding) === 'US-ASCII') + { + $this->encoding = 'UTF-8'; + } + else + { + $this->encoding = $encoding; + } - static $cache; - if (!isset($cache[get_class($this)])) + // Strip BOM: + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") { - $all_methods = get_class_methods($this); + $data = substr($data, 4); + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $data = substr($data, 4); + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $data = substr($data, 2); + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $data = substr($data, 2); + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $data = substr($data, 3); + } - foreach ($all_methods as $method) + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) + { + $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); + if ($declaration->parse()) { - if (strtolower(substr($method, 0, 5)) === 'date_') - { - $cache[get_class($this)][] = $method; - } + $data = substr($data, $pos + 2); + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; + } + else + { + $this->error_string = 'SimplePie bug! Please report this!'; + return false; } } - foreach ($cache[get_class($this)] as $method) + $return = true; + + static $xml_is_sane = null; + if ($xml_is_sane === null) { - $this->built_in[] = $method; + $parser_check = xml_parser_create(); + xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); + xml_parser_free($parser_check); + $xml_is_sane = isset($values[0]['value']); } - } - /** - * Get the object - * - * @access public - */ - function get() - { - static $object; - if (!$object) + // Create the parser + if ($xml_is_sane) { - $object = new SimplePie_Parse_Date; - } - return $object; - } + $xml = xml_parser_create_ns($this->encoding, $this->separator); + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xml, $this); + xml_set_character_data_handler($xml, 'cdata'); + xml_set_element_handler($xml, 'tag_open', 'tag_close'); - /** - * Parse a date - * - * @final - * @access public - * @param string $date Date to parse - * @return int Timestamp corresponding to date string, or false on failure - */ - function parse($date) - { - foreach ($this->user as $method) + // Parse! + if (!xml_parse($xml, $data, true)) + { + $this->error_code = xml_get_error_code($xml); + $this->error_string = xml_error_string($this->error_code); + $return = false; + } + $this->current_line = xml_get_current_line_number($xml); + $this->current_column = xml_get_current_column_number($xml); + $this->current_byte = xml_get_current_byte_index($xml); + xml_parser_free($xml); + return $return; + } + else { - if (($returned = call_user_func($method, $date)) !== false) + libxml_clear_errors(); + $xml = new XMLReader(); + $xml->xml($data); + while (@$xml->read()) { - return $returned; + switch ($xml->nodeType) + { + + case constant('XMLReader::END_ELEMENT'): + if ($xml->namespaceURI !== '') + { + $tagName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $tagName = $xml->localName; + } + $this->tag_close(null, $tagName); + break; + case constant('XMLReader::ELEMENT'): + $empty = $xml->isEmptyElement; + if ($xml->namespaceURI !== '') + { + $tagName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $tagName = $xml->localName; + } + $attributes = array(); + while ($xml->moveToNextAttribute()) + { + if ($xml->namespaceURI !== '') + { + $attrName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $attrName = $xml->localName; + } + $attributes[$attrName] = $xml->value; + } + $this->tag_open(null, $tagName, $attributes); + if ($empty) + { + $this->tag_close(null, $tagName); + } + break; + case constant('XMLReader::TEXT'): + + case constant('XMLReader::CDATA'): + $this->cdata(null, $xml->value); + break; + } + } + if ($error = libxml_get_last_error()) + { + $this->error_code = $error->code; + $this->error_string = $error->message; + $this->current_line = $error->line; + $this->current_column = $error->column; + return false; + } + else + { + return true; } } + } + + public function get_error_code() + { + return $this->error_code; + } + + public function get_error_string() + { + return $this->error_string; + } + + public function get_current_line() + { + return $this->current_line; + } - foreach ($this->built_in as $method) - { - if (($returned = call_user_func(array(&$this, $method), $date)) !== false) - { - return $returned; - } - } + public function get_current_column() + { + return $this->current_column; + } - return false; + public function get_current_byte() + { + return $this->current_byte; } - /** - * Add a callback method to parse a date - * - * @final - * @access public - * @param callback $callback - */ - function add_callback($callback) + public function get_data() { - if (is_callable($callback)) - { - $this->user[] = $callback; - } - else - { - trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); - } + return $this->data; } - /** - * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as - * well as allowing any of upper or lower case "T", horizontal tabs, or - * spaces to be used as the time seperator (including more than one)) - * - * @access protected - * @return int Timestamp - */ - function date_w3cdtf($date) + public function tag_open($parser, $tag, $attributes) { - static $pcre; - if (!$pcre) + list($this->namespace[], $this->element[]) = $this->split_ns($tag); + + $attribs = array(); + foreach ($attributes as $name => $value) { - $year = '([0-9]{4})'; - $month = $day = $hour = $minute = $second = '([0-9]{2})'; - $decimal = '([0-9]*)'; - $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; - $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; + list($attrib_namespace, $attribute) = $this->split_ns($name); + $attribs[$attrib_namespace][$attribute] = $value; } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Year - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Decimal fraction of a second - 8: Zulu - 9: Timezone ± - 10: Timezone hours - 11: Timezone minutes - */ - - // Fill in empty matches - for ($i = count($match); $i <= 3; $i++) - { - $match[$i] = '1'; - } - - for ($i = count($match); $i <= 7; $i++) - { - $match[$i] = '0'; - } - // Numeric timezone - if (isset($match[9]) && $match[9] !== '') - { - $timezone = $match[10] * 3600; - $timezone += $match[11] * 60; - if ($match[9] === '-') - { - $timezone = 0 - $timezone; - } - } - else + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) + { + $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base))); + if ($base !== false) { - $timezone = 0; + $this->xml_base[] = $base; + $this->xml_base_explicit[] = true; } - - // Convert the number of seconds to an integer, taking decimals into account - $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); - - return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; } else { - return false; + $this->xml_base[] = end($this->xml_base); + $this->xml_base_explicit[] = end($this->xml_base_explicit); } - } - - /** - * Remove RFC822 comments - * - * @access protected - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function remove_rfc2822_comments($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; - $output = ''; + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) + { + $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; + } + else + { + $this->xml_lang[] = end($this->xml_lang); + } - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + if ($this->current_xhtml_construct >= 0) { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') + $this->current_xhtml_construct++; + if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML) { - $depth++; - while ($depth && $position < $length) + $this->data['data'] .= '<' . end($this->element); + if (isset($attribs[''])) { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; - - case ')': - $depth--; - break; - } - $position++; - } - else + foreach ($attribs[''] as $name => $value) { - break; + $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; } } + $this->data['data'] .= '>'; } - else + } + else + { + $this->datas[] =& $this->data; + $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; + $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); + if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml') + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml') + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title'))) + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title'))) + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title')))) { - $output .= '('; + $this->current_xhtml_construct = 0; } } - $output .= substr($string, $position); - - return $output; } - /** - * Parse RFC2822's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc2822($date) + public function cdata($parser, $cdata) { - static $pcre; - if (!$pcre) - { - $wsp = '[\x09\x20]'; - $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; - $optional_fws = $fws . '?'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $minute = $second = '([0-9]{2})'; - $year = '([0-9]{2,4})'; - $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; - $character_zone = '([A-Z]{1,5})'; - $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; - $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; - } - if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone ± - 9: Timezone hours - 10: Timezone minutes - 11: Alphabetic timezone - */ - - // Find the month number - $month = $this->month[strtolower($match[3])]; - - // Numeric timezone - if ($match[8] !== '') - { - $timezone = $match[9] * 3600; - $timezone += $match[10] * 60; - if ($match[8] === '-') - { - $timezone = 0 - $timezone; - } - } - // Character timezone - elseif (isset($this->timezone[strtoupper($match[11])])) - { - $timezone = $this->timezone[strtoupper($match[11])]; - } - // Assume everything else to be -0000 - else - { - $timezone = 0; - } - - // Deal with 2/3 digit years - if ($match[4] < 50) - { - $match[4] += 2000; - } - elseif ($match[4] < 1000) - { - $match[4] += 1900; - } - - // Second is optional, if it is empty set it to zero - if ($match[7] !== '') - { - $second = $match[7]; - } - else - { - $second = 0; - } - - return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; + if ($this->current_xhtml_construct >= 0) + { + $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); } else { - return false; + $this->data['data'] .= $cdata; } } - /** - * Parse RFC850's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc850($date) + public function tag_close($parser, $tag) { - static $pcre; - if (!$pcre) + if ($this->current_xhtml_construct >= 0) { - $space = '[\x09\x20]+'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $year = $hour = $minute = $second = '([0-9]{2})'; - $zone = '([A-Z]{1,5})'; - $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; + $this->current_xhtml_construct--; + if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) + { + $this->data['data'] .= '</' . end($this->element) . '>'; + } } - if (preg_match($pcre, $date, $match)) + if ($this->current_xhtml_construct === -1) { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone - */ + $this->data =& $this->datas[count($this->datas) - 1]; + array_pop($this->datas); + } - // Month - $month = $this->month[strtolower($match[3])]; + array_pop($this->element); + array_pop($this->namespace); + array_pop($this->xml_base); + array_pop($this->xml_base_explicit); + array_pop($this->xml_lang); + } - // Character timezone - if (isset($this->timezone[strtoupper($match[8])])) - { - $timezone = $this->timezone[strtoupper($match[8])]; - } - // Assume everything else to be -0000 - else + public function split_ns($string) + { + static $cache = array(); + if (!isset($cache[$string])) + { + if ($pos = strpos($string, $this->separator)) { - $timezone = 0; - } + static $separator_length; + if (!$separator_length) + { + $separator_length = strlen($this->separator); + } + $namespace = substr($string, 0, $pos); + $local_name = substr($string, $pos + $separator_length); + if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) + { + $namespace = SIMPLEPIE_NAMESPACE_ITUNES; + } - // Deal with 2 digit year - if ($match[4] < 50) - { - $match[4] += 2000; + // Normalize the Media RSS namespaces + if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 ) + { + $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; + } + $cache[$string] = array($namespace, $local_name); } else { - $match[4] += 1900; + $cache[$string] = array('', $string); } - - return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; - } - else - { - return false; } + return $cache[$string]; } +} + +/** + * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively + * + * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()} + * + * This class can be overloaded with {@see SimplePie::set_rating_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Rating +{ + /** + * Rating scheme + * + * @var string + * @see get_scheme() + */ + var $scheme; /** - * Parse C99's asctime()'s date format + * Rating value * - * @access protected - * @return int Timestamp + * @var string + * @see get_value() */ - function date_asctime($date) + var $value; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($scheme = null, $value = null) { - static $pcre; - if (!$pcre) - { - $space = '[\x09\x20]+'; - $wday_name = $this->day_pcre; - $mon_name = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $sec = $min = '([0-9]{2})'; - $year = '([0-9]{4})'; - $terminator = '\x0A?\x00?'; - $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Year - */ + $this->scheme = $scheme; + $this->value = $value; + } - $month = $this->month[strtolower($match[2])]; - return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the organizational scheme for the rating + * + * @return string|null + */ + public function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; } else { - return false; + return null; } } /** - * Parse dates using strtotime() + * Get the value of the rating * - * @access protected - * @return int Timestamp + * @return string|null */ - function date_strtotime($date) + public function get_value() { - $strtotime = strtotime($date); - if ($strtotime === -1 || $strtotime === false) + if ($this->value !== null) { - return false; + return $this->value; } else { - return $strtotime; + return null; } } } /** - * Content-type sniffing + * Handles creating objects and calling methods + * + * Access this via {@see SimplePie::get_registry()} * * @package SimplePie */ -class SimplePie_Content_Type_Sniffer +class SimplePie_Registry { /** - * File object + * Default class mapping * - * @var SimplePie_File - * @access private + * Overriding classes *must* subclass these. + * + * @var array */ - var $file; + protected $default = array( + 'Cache' => 'SimplePie_Cache', + 'Locator' => 'SimplePie_Locator', + 'Parser' => 'SimplePie_Parser', + 'File' => 'SimplePie_File', + 'Sanitize' => 'SimplePie_Sanitize', + 'Item' => 'SimplePie_Item', + 'Author' => 'SimplePie_Author', + 'Category' => 'SimplePie_Category', + 'Enclosure' => 'SimplePie_Enclosure', + 'Caption' => 'SimplePie_Caption', + 'Copyright' => 'SimplePie_Copyright', + 'Credit' => 'SimplePie_Credit', + 'Rating' => 'SimplePie_Rating', + 'Restriction' => 'SimplePie_Restriction', + 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer', + 'Source' => 'SimplePie_Source', + 'Misc' => 'SimplePie_Misc', + 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser', + 'Parse_Date' => 'SimplePie_Parse_Date', + ); /** - * Create an instance of the class with the input file + * Class mapping * - * @access public - * @param SimplePie_Content_Type_Sniffer $file Input file + * @see register() + * @var array */ - function SimplePie_Content_Type_Sniffer($file) - { - $this->file = $file; - } + protected $classes = array(); /** - * Get the Content-Type of the specified file + * Legacy classes * - * @access public - * @return string Actual Content-Type + * @see register() + * @var array */ - function get_type() - { - if (isset($this->file->headers['content-type'])) - { - if (!isset($this->file->headers['content-encoding']) - && ($this->file->headers['content-type'] === 'text/plain' - || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' - || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1')) - { - return $this->text_or_binary(); - } - - if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) - { - $official = substr($this->file->headers['content-type'], 0, $pos); - } - else - { - $official = $this->file->headers['content-type']; - } - $official = strtolower($official); - - if ($official === 'unknown/unknown' - || $official === 'application/unknown') - { - return $this->unknown(); - } - elseif (substr($official, -4) === '+xml' - || $official === 'text/xml' - || $official === 'application/xml') - { - return $official; - } - elseif (substr($official, 0, 6) === 'image/') - { - if ($return = $this->image()) - { - return $return; - } - else - { - return $official; - } - } - elseif ($official === 'text/html') - { - return $this->feed_or_html(); - } - else - { - return $official; - } - } - else - { - return $this->unknown(); - } - } + protected $legacy = array(); /** - * Sniff text or binary + * Constructor * - * @access private - * @return string Actual Content-Type + * No-op + */ + public function __construct() { } + + /** + * Register a class + * + * @param string $type See {@see $default} for names + * @param string $class Class name, must subclass the corresponding default + * @param bool $legacy Whether to enable legacy support for this class + * @return bool Successfulness */ - function text_or_binary() + public function register($type, $class, $legacy = false) { - if (substr($this->file->body, 0, 2) === "\xFE\xFF" - || substr($this->file->body, 0, 2) === "\xFF\xFE" - || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" - || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") - { - return 'text/plain'; - } - elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) + if (!is_subclass_of($class, $this->default[$type])) { - return 'application/octect-stream'; + return false; } - else + + $this->classes[$type] = $class; + + if ($legacy) { - return 'text/plain'; + $this->legacy[] = $class; } + + return true; } /** - * Sniff unknown + * Get the class registered for a type * - * @access private - * @return string Actual Content-Type + * Where possible, use {@see create()} or {@see call()} instead + * + * @param string $type + * @return string|null */ - function unknown() + public function get_class($type) { - $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); - if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html' - || strtolower(substr($this->file->body, $ws, 5)) === '<html' - || strtolower(substr($this->file->body, $ws, 7)) === '<script') - { - return 'text/html'; - } - elseif (substr($this->file->body, 0, 5) === '%PDF-') - { - return 'application/pdf'; - } - elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') - { - return 'application/postscript'; - } - elseif (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") - { - return 'image/png'; - } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") - { - return 'image/jpeg'; - } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + if (!empty($this->classes[$type])) { - return 'image/bmp'; + return $this->classes[$type]; } - else + if (!empty($this->default[$type])) { - return $this->text_or_binary(); + return $this->default[$type]; } + + return null; } /** - * Sniff images + * Create a new instance of a given type * - * @access private - * @return string Actual Content-Type + * @param string $type + * @param array $parameters Parameters to pass to the constructor + * @return object Instance of class */ - function image() + public function &create($type, $parameters = array()) { - if (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + $class = $this->get_class($type); + + if (in_array($class, $this->legacy)) { - return 'image/png'; + switch ($type) + { + case 'locator': + // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class + // Specified: file, timeout, useragent, max_checked_feeds + $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer')); + array_splice($parameters, 3, 1, $replacement); + break; + } } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + + if (!method_exists($class, '__construct')) { - return 'image/jpeg'; + $instance = new $class; } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + else { - return 'image/bmp'; + $reflector = new ReflectionClass($class); + $instance = $reflector->newInstanceArgs($parameters); } - else + + if (method_exists($instance, 'set_registry')) { - return false; + $instance->set_registry($this); } + return $instance; } /** - * Sniff HTML + * Call a static method for a type * - * @access private - * @return string Actual Content-Type + * @param string $type + * @param string $method + * @param array $parameters + * @return mixed */ - function feed_or_html() + public function &call($type, $method, $parameters = array()) { - $len = strlen($this->file->body); - $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); + $class = $this->get_class($type); - while ($pos < $len) + if (in_array($class, $this->legacy)) { - switch ($this->file->body[$pos]) + switch ($type) { - case "\x09": - case "\x0A": - case "\x0D": - case "\x20": - $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); - continue 2; - - case '<': - $pos++; + case 'Cache': + // For backwards compatibility with old non-static + // Cache::create() methods + if ($method === 'get_handler') + { + $result = @call_user_func_array(array($class, 'create'), $parameters); + return $result; + } break; - - default: - return 'text/html'; - } - - if (substr($this->file->body, $pos, 3) === '!--') - { - $pos += 3; - if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) - { - $pos += 3; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '!') - { - if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) - { - $pos++; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '?') - { - if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) - { - $pos += 2; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 3) === 'rss' - || substr($this->file->body, $pos, 7) === 'rdf:RDF') - { - return 'application/rss+xml'; - } - elseif (substr($this->file->body, $pos, 4) === 'feed') - { - return 'application/atom+xml'; - } - else - { - return 'text/html'; } } - return 'text/html'; + $result = call_user_func_array(array($class, $method), $parameters); + return $result; } } /** - * Parses the XML Declaration + * Handles `<media:restriction>` as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()} + * + * This class can be overloaded with {@see SimplePie::set_restriction_class()} * * @package SimplePie + * @subpackage API */ -class SimplePie_XML_Declaration_Parser +class SimplePie_Restriction { /** - * XML Version + * Relationship ('allow'/'deny') * - * @access public * @var string + * @see get_relationship() */ - var $version = '1.0'; + var $relationship; /** - * Encoding + * Type of restriction * - * @access public * @var string + * @see get_type() */ - var $encoding = 'UTF-8'; + var $type; /** - * Standalone + * Restricted values * - * @access public - * @var bool + * @var string + * @see get_value() */ - var $standalone = false; + var $value; /** - * Current state of the state machine + * Constructor, used to input the data * - * @access private - * @var string + * For documentation on all the parameters, see the corresponding + * properties and their accessors */ - var $state = 'before_version_name'; + public function __construct($relationship = null, $type = null, $value = null) + { + $this->relationship = $relationship; + $this->type = $type; + $this->value = $value; + } /** - * Input data + * String-ified version * - * @access private - * @var string + * @return string */ - var $data = ''; + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } /** - * Input data length (to avoid calling strlen() everytime this is needed) + * Get the relationship * - * @access private - * @var int + * @return string|null Either 'allow' or 'deny' */ - var $data_length = 0; + public function get_relationship() + { + if ($this->relationship !== null) + { + return $this->relationship; + } + else + { + return null; + } + } /** - * Current position of the pointer + * Get the type * - * @var int - * @access private + * @return string|null */ - var $position = 0; + public function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + /** + * Get the list of restricted things + * + * @return string|null + */ + public function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} + +/** + * Used for data cleanup and post-processing + * + * + * This class can be overloaded with {@see SimplePie::set_sanitize_class()} + * + * @package SimplePie + * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags + */ +class SimplePie_Sanitize +{ + // Private vars + var $base; + + // Options + var $remove_div = true; + var $image_handler = ''; + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + var $encode_instead_of_strip = false; + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $strip_comments = false; + var $output_encoding = 'UTF-8'; + var $enable_cache = true; + var $cache_location = './cache'; + var $cache_name_function = 'md5'; + var $timeout = 10; + var $useragent = ''; + var $force_fsockopen = false; + var $replace_url_attributes = null; - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_XML_Declaration_Parser($data) + public function __construct() { - $this->data = $data; - $this->data_length = strlen($this->data); + // Set defaults + $this->set_url_replacements(null); } - /** - * Parse the input data - * - * @access public - * @return bool true on success, false on failure - */ - function parse() + public function remove_div($enable = true) { - while ($this->state && $this->state !== 'emit' && $this->has_data()) - { - $state = $this->state; - $this->$state(); - } - $this->data = ''; - if ($this->state === 'emit') + $this->remove_div = (bool) $enable; + } + + public function set_image_handler($page = false) + { + if ($page) { - return true; + $this->image_handler = (string) $page; } else { - $this->version = ''; - $this->encoding = ''; - $this->standalone = ''; - return false; + $this->image_handler = false; } } - /** - * Check whether there is data beyond the pointer - * - * @access private - * @return bool true if there is further data, false if not - */ - function has_data() + public function set_registry(SimplePie_Registry $registry) { - return (bool) ($this->position < $this->data_length); + $this->registry = $registry; } - /** - * Advance past any whitespace - * - * @return int Number of whitespace characters passed - */ - function skip_whitespace() + public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') { - $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); - $this->position += $whitespace; - return $whitespace; + if (isset($enable_cache)) + { + $this->enable_cache = (bool) $enable_cache; + } + + if ($cache_location) + { + $this->cache_location = (string) $cache_location; + } + + if ($cache_name_function) + { + $this->cache_name_function = (string) $cache_name_function; + } } - /** - * Read value - */ - function get_value() + public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) { - $quote = substr($this->data, $this->position, 1); - if ($quote === '"' || $quote === "'") + if ($timeout) { - $this->position++; - $len = strcspn($this->data, $quote, $this->position); - if ($this->has_data()) + $this->timeout = (string) $timeout; + } + + if ($useragent) + { + $this->useragent = (string) $useragent; + } + + if ($force_fsockopen) + { + $this->force_fsockopen = (string) $force_fsockopen; + } + } + + public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) + { + if ($tags) + { + if (is_array($tags)) { - $value = substr($this->data, $this->position, $len); - $this->position += $len + 1; - return $value; + $this->strip_htmltags = $tags; + } + else + { + $this->strip_htmltags = explode(',', $tags); } } - return false; + else + { + $this->strip_htmltags = false; + } } - function before_version_name() + public function encode_instead_of_strip($encode = false) { - if ($this->skip_whitespace()) + $this->encode_instead_of_strip = (bool) $encode; + } + + public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) + { + if ($attribs) { - $this->state = 'version_name'; + if (is_array($attribs)) + { + $this->strip_attributes = $attribs; + } + else + { + $this->strip_attributes = explode(',', $attribs); + } } else { - $this->state = false; + $this->strip_attributes = false; + } + } + + public function strip_comments($strip = false) + { + $this->strip_comments = (bool) $strip; + } + + public function set_output_encoding($encoding = 'UTF-8') + { + $this->output_encoding = (string) $encoding; + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, + * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, + * |q|@cite + * + * @since 1.0 + * @param array|null $element_attribute Element/attribute key/value pairs, null for default + */ + public function set_url_replacements($element_attribute = null) + { + if ($element_attribute === null) + { + $element_attribute = array( + 'a' => 'href', + 'area' => 'href', + 'blockquote' => 'cite', + 'del' => 'cite', + 'form' => 'action', + 'img' => array( + 'longdesc', + 'src' + ), + 'input' => 'src', + 'ins' => 'cite', + 'q' => 'cite' + ); + } + $this->replace_url_attributes = (array) $element_attribute; + } + + public function sanitize($data, $type, $base = '') + { + $data = trim($data); + if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) + { + if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) + { + if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) + { + $type |= SIMPLEPIE_CONSTRUCT_HTML; + } + else + { + $type |= SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_BASE64) + { + $data = base64_decode($data); + } + + if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) + { + + $document = new DOMDocument(); + $document->encoding = 'UTF-8'; + $data = $this->preprocess($data, $type); + + set_error_handler(array('SimplePie_Misc', 'silence_errors')); + $document->loadHTML($data); + restore_error_handler(); + + // Strip comments + if ($this->strip_comments) + { + $xpath = new DOMXPath($document); + $comments = $xpath->query('//comment()'); + + foreach ($comments as $comment) + { + $comment->parentNode->removeChild($comment); + } + } + + // Strip out HTML tags and attributes that might cause various security problems. + // Based on recommendations by Mark Pilgrim at: + // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely + if ($this->strip_htmltags) + { + foreach ($this->strip_htmltags as $tag) + { + $this->strip_tag($tag, $document, $type); + } + } + + if ($this->strip_attributes) + { + foreach ($this->strip_attributes as $attrib) + { + $this->strip_attr($attrib, $document); + } + } + + // Replace relative URLs + $this->base = $base; + foreach ($this->replace_url_attributes as $element => $attributes) + { + $this->replace_urls($document, $element, $attributes); + } + + // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. + if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) + { + $images = $document->getElementsByTagName('img'); + foreach ($images as $img) + { + if ($img->hasAttribute('src')) + { + $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src')); + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi')); + + if ($cache->load()) + { + $img->setAttribute('src', $this->image_handler . $image_url); + } + else + { + $file = $this->registry->create('File', array($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen)); + $headers = $file->headers; + + if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + $img->setAttribute('src', $this->image_handler . $image_url); + } + else + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + } + } + } + } + } + + // Remove the DOCTYPE + // Seems to cause segfaulting if we don't do this + if ($document->firstChild instanceof DOMDocumentType) + { + $document->removeChild($document->firstChild); + } + + // Move everything from the body to the root + $real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0); + $document->replaceChild($real_body, $document->firstChild); + + // Finally, convert to a HTML string + $data = trim($document->saveHTML()); + + if ($this->remove_div) + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data); + $data = preg_replace('/<\/div>$/', '', $data); + } + else + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data); + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_IRI) + { + $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base)); + if ($absolute !== false) + { + $data = $absolute; + } + } + + if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) + { + $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); + } + + if ($this->output_encoding !== 'UTF-8') + { + $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding)); + } } + return $data; } - function version_name() + protected function preprocess($html, $type) { - if (substr($this->data, $this->position, 7) === 'version') + $ret = ''; + if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML) { - $this->position += 7; - $this->skip_whitespace(); - $this->state = 'version_equals'; + // Atom XHTML constructs are wrapped with a div by default + // Note: No protection if $html contains a stray </div>! + $html = '<div>' . $html . '</div>'; + $ret .= '<!DOCTYPE html>'; + $content_type = 'text/html'; } else { - $this->state = false; + $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; + $content_type = 'application/xhtml+xml'; } + + $ret .= '<html><head>'; + $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />'; + $ret .= '</head><body>' . $html . '</body></html>'; + return $ret; } - function version_equals() + public function replace_urls($document, $tag, $attributes) { - if (substr($this->data, $this->position, 1) === '=') + if (!is_array($attributes)) { - $this->position++; - $this->skip_whitespace(); - $this->state = 'version_value'; + $attributes = array($attributes); } - else + + if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) { - $this->state = false; + $elements = $document->getElementsByTagName($tag); + foreach ($elements as $element) + { + foreach ($attributes as $attribute) + { + if ($element->hasAttribute($attribute)) + { + $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base)); + if ($value !== false) + { + $element->setAttribute($attribute, $value); + } + } + } + } } } - function version_value() + public function do_strip_htmltags($match) { - if ($this->version = $this->get_value()) + if ($this->encode_instead_of_strip) { - $this->skip_whitespace(); - if ($this->has_data()) + if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) { - $this->state = 'encoding_name'; + $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); + $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); + return "<$match[1]$match[2]>$match[3]</$match[1]>"; } else { - $this->state = 'emit'; + return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); } } + elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + return $match[4]; + } else { - $this->state = false; + return ''; } } - function encoding_name() + protected function strip_tag($tag, $document, $type) { - if (substr($this->data, $this->position, 8) === 'encoding') + $xpath = new DOMXPath($document); + $elements = $xpath->query('body//' . $tag); + if ($this->encode_instead_of_strip) { - $this->position += 8; - $this->skip_whitespace(); - $this->state = 'encoding_equals'; + foreach ($elements as $element) + { + $fragment = $document->createDocumentFragment(); + + // For elements which aren't script or style, include the tag itself + if (!in_array($tag, array('script', 'style'))) + { + $text = '<' . $tag; + if ($element->hasAttributes()) + { + $attrs = array(); + foreach ($element->attributes as $name => $attr) + { + $value = $attr->value; + + // In XHTML, empty values should never exist, so we repeat the value + if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML)) + { + $value = $name; + } + // For HTML, empty is fine + elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML)) + { + $attrs[] = $name; + continue; + } + + // Standard attribute text + $attrs[] = $name . '="' . $attr->value . '"'; + } + $text .= ' ' . implode(' ', $attrs); + } + $text .= '>'; + $fragment->appendChild(new DOMText($text)); + } + + $number = $element->childNodes->length; + for ($i = $number; $i > 0; $i--) + { + $child = $element->childNodes->item(0); + $fragment->appendChild($child); + } + + if (!in_array($tag, array('script', 'style'))) + { + $fragment->appendChild(new DOMText('</' . $tag . '>')); + } + + $element->parentNode->replaceChild($fragment, $element); + } + + return; + } + elseif (in_array($tag, array('script', 'style'))) + { + foreach ($elements as $element) + { + $element->parentNode->removeChild($element); + } + + return; } else { - $this->state = 'standalone_name'; + foreach ($elements as $element) + { + $fragment = $document->createDocumentFragment(); + $number = $element->childNodes->length; + for ($i = $number; $i > 0; $i--) + { + $child = $element->childNodes->item(0); + $fragment->appendChild($child); + } + + $element->parentNode->replaceChild($fragment, $element); + } } } - function encoding_equals() + protected function strip_attr($attrib, $document) { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'encoding_value'; - } - else + $xpath = new DOMXPath($document); + $elements = $xpath->query('//*[@' . $attrib . ']'); + + foreach ($elements as $element) { - $this->state = false; + $element->removeAttribute($attrib); } } +} + +/** + * Handles `<atom:source>` + * + * Used by {@see SimplePie_Item::get_source()} + * + * This class can be overloaded with {@see SimplePie::set_source_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Source +{ + var $item; + var $data = array(); + protected $registry; + + public function __construct($item, $data) + { + $this->item = $item; + $this->data = $data; + } - function encoding_value() + public function set_registry(SimplePie_Registry $registry) { - if ($this->encoding = $this->get_value()) + $this->registry = $registry; + } + + public function __toString() + { + return md5(serialize($this->data)); + } + + public function get_source_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) { - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = 'standalone_name'; - } - else - { - $this->state = 'emit'; - } + return $this->data['child'][$namespace][$tag]; } else { - $this->state = false; + return null; } } - function standalone_name() + public function get_base($element = array()) { - if (substr($this->data, $this->position, 10) === 'standalone') + return $this->item->get_base($element); + } + + public function sanitize($data, $type, $base = '') + { + return $this->item->sanitize($data, $type, $base); + } + + public function get_item() + { + return $this->item; + } + + public function get_title() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { - $this->position += 10; - $this->skip_whitespace(); - $this->state = 'standalone_equals'; + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { - $this->state = false; + return null; } } - function standalone_equals() + public function get_category($key = 0) { - if (substr($this->data, $this->position, 1) === '=') + $categories = $this->get_categories(); + if (isset($categories[$key])) { - $this->position++; - $this->skip_whitespace(); - $this->state = 'standalone_value'; + return $categories[$key]; } else { - $this->state = false; + return null; } } - function standalone_value() + public function get_categories() { - if ($standalone = $this->get_value()) + $categories = array(); + + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) { - switch ($standalone) + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) { - case 'yes': - $this->standalone = true; - break; - - case 'no': - $this->standalone = false; - break; - - default: - $this->state = false; - return; + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); } - - $this->skip_whitespace(); - if ($this->has_data()) + if (isset($category['attribs']['']['scheme'])) { - $this->state = false; + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } - else + if (isset($category['attribs']['']['label'])) { - $this->state = 'emit'; + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } - else + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) { - $this->state = false; + // This is really the label, but keep this as the term also for BC. + // Label will also work on retrieving because that falls back to term. + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($category['attribs']['']['domain'])) + { + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = null; + } + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); } - } -} - -class SimplePie_Locator -{ - var $useragent; - var $timeout; - var $file; - var $local = array(); - var $elsewhere = array(); - var $file_class = 'SimplePie_File'; - var $cached_entities = array(); - var $http_base; - var $base; - var $base_location = 0; - var $checked_feeds = 0; - var $max_checked_feeds = 10; - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer') - { - $this->file =& $file; - $this->file_class = $file_class; - $this->useragent = $useragent; - $this->timeout = $timeout; - $this->max_checked_feeds = $max_checked_feeds; - $this->content_type_sniffer_class = $content_type_sniffer_class; - } - - function find($type = SIMPLEPIE_LOCATOR_ALL, &$working) - { - if ($this->is_feed($this->file)) + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) { - return $this->file; + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } - if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + if (!empty($categories)) { - $sniffer = new $this->content_type_sniffer_class($this->file); - if ($sniffer->get_type() !== 'text/html') - { - return null; - } + return array_unique($categories); } - - if ($type & ~SIMPLEPIE_LOCATOR_NONE) + else { - $this->get_base(); + return null; } + } - if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) + public function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) { - return $working[0]; + return $authors[$key]; + } + else + { + return null; } + } - if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) + public function get_authors() + { + $authors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) { - if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { - return $working; + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { - return $working; + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { - return $working; + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) + if ($name !== null || $email !== null || $uri !== null) { - return $working; + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); } } - return null; - } - - function is_feed(&$file) - { - if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) { - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); - if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { - return true; + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - else + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { - return false; + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } - elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { - return true; + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($authors)) + { + return array_unique($authors); } else { - return false; + return null; } } - function get_base() + public function get_contributor($key = 0) { - $this->http_base = $this->file->url; - $this->base = $this->http_base; - $elements = SimplePie_Misc::get_element('base', $this->file->body); - foreach ($elements as $element) + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) { - if ($element['attribs']['href']['data'] !== '') - { - $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base); - $this->base_location = $element['offset']; - break; - } + return $contributors[$key]; + } + else + { + return null; } } - function autodiscovery() + public function get_contributors() { - $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body)); - $done = array(); - $feeds = array(); - foreach ($links as $link) + $contributors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) { - if ($this->checked_feeds === $this->max_checked_feeds) + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { - break; + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data'])) + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { - $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data']))); - - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href])) - { - $this->checked_feeds++; - $feed = new $this->file_class($href, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) - { - $feeds[$href] = $feed; - } - } - $done[] = $href; + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); } } - if (!empty($feeds)) + if (!empty($contributors)) { - return array_values($feeds); + return array_unique($contributors); } - else { + else + { return null; } } - function get_links() + public function get_link($key = 0, $rel = 'alternate') { - $links = SimplePie_Misc::get_element('a', $this->file->body); - foreach ($links as $link) + $links = $this->get_links($rel); + if (isset($links[$key])) { - if (isset($link['attribs']['href']['data'])) - { - $href = trim($link['attribs']['href']['data']); - $parsed = SimplePie_Misc::parse_url($href); - if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) - { - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - $current = SimplePie_Misc::parse_url($this->file->url); - - if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) - { - $this->local[] = $href; - } - else - { - $this->elsewhere[] = $href; - } - } - } + return $links[$key]; } - $this->local = array_unique($this->local); - $this->elsewhere = array_unique($this->elsewhere); - if (!empty($this->local) || !empty($this->elsewhere)) + else { - return true; + return null; } - return null; } - function extension(&$array) + /** + * Added for parity between the parent-level and the item/entry-level. + */ + public function get_permalink() + { + return $this->get_link(0); + } + + public function get_links($rel = 'alternate') { - foreach ($array as $key => $value) + if (!isset($this->data['links'])) { - if ($this->checked_feeds === $this->max_checked_feeds) - { - break; - } - if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) + $this->data['links'] = array(); + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) { - $this->checked_feeds++; - $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + foreach ($links as $link) { - return $feed; + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } } - else + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) { - unset($array[$key]); + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } } } - } - return null; - } - - function body(&$array) - { - foreach ($array as $key => $value) - { - if ($this->checked_feeds === $this->max_checked_feeds) + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { - break; + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } - if (preg_match('/(rss|rdf|atom|xml)/i', $value)) + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) { - $this->checked_feeds++; - $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { - return $feed; + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } } - else + elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) { - unset($array[$key]); + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); } } - return null; - } -} - -class SimplePie_Parser -{ - var $error_code; - var $error_string; - var $current_line; - var $current_column; - var $current_byte; - var $separator = ' '; - var $namespace = array(''); - var $element = array(''); - var $xml_base = array(''); - var $xml_base_explicit = array(false); - var $xml_lang = array(''); - var $data = array(); - var $datas = array(array()); - var $current_xhtml_construct = -1; - var $encoding; - function parse(&$data, $encoding) - { - // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character - if (strtoupper($encoding) === 'US-ASCII') + if (isset($this->data['links'][$rel])) { - $this->encoding = 'UTF-8'; + return $this->data['links'][$rel]; } else { - $this->encoding = $encoding; + return null; } + } - // Strip BOM: - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + public function get_description() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) { - $data = substr($data, 4); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) { - $data = substr($data, 4); + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) { - $data = substr($data, 2); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) { - $data = substr($data, 2); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) { - $data = substr($data, 3); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } - - if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) { - $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($declaration->parse()) - { - $data = substr($data, $pos + 2); - $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; - } - else - { - $this->error_string = 'SimplePie bug! Please report this!'; - return false; - } + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - $return = true; - - static $xml_is_sane = null; - if ($xml_is_sane === null) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) { - $parser_check = xml_parser_create(); - xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); - xml_parser_free($parser_check); - $xml_is_sane = isset($values[0]['value']); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - // Create the parser - if ($xml_is_sane) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) { - $xml = xml_parser_create_ns($this->encoding, $this->separator); - xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); - xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); - xml_set_object($xml, $this); - xml_set_character_data_handler($xml, 'cdata'); - xml_set_element_handler($xml, 'tag_open', 'tag_close'); - - // Parse! - if (!xml_parse($xml, $data, true)) - { - $this->error_code = xml_get_error_code($xml); - $this->error_string = xml_error_string($this->error_code); - $return = false; - } - $this->current_line = xml_get_current_line_number($xml); - $this->current_column = xml_get_current_column_number($xml); - $this->current_byte = xml_get_current_byte_index($xml); - xml_parser_free($xml); - return $return; + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } else { - libxml_clear_errors(); - $xml = new XMLReader(); - $xml->xml($data); - while (@$xml->read()) - { - switch ($xml->nodeType) - { - - case constant('XMLReader::END_ELEMENT'): - if ($xml->namespaceURI !== '') - { - $tagName = $xml->namespaceURI . $this->separator . $xml->localName; - } - else - { - $tagName = $xml->localName; - } - $this->tag_close(null, $tagName); - break; - case constant('XMLReader::ELEMENT'): - $empty = $xml->isEmptyElement; - if ($xml->namespaceURI !== '') - { - $tagName = $xml->namespaceURI . $this->separator . $xml->localName; - } - else - { - $tagName = $xml->localName; - } - $attributes = array(); - while ($xml->moveToNextAttribute()) - { - if ($xml->namespaceURI !== '') - { - $attrName = $xml->namespaceURI . $this->separator . $xml->localName; - } - else - { - $attrName = $xml->localName; - } - $attributes[$attrName] = $xml->value; - } - $this->tag_open(null, $tagName, $attributes); - if ($empty) - { - $this->tag_close(null, $tagName); - } - break; - case constant('XMLReader::TEXT'): - - case constant('XMLReader::CDATA'): - $this->cdata(null, $xml->value); - break; - } - } - if ($error = libxml_get_last_error()) - { - $this->error_code = $error->code; - $this->error_string = $error->message; - $this->current_line = $error->line; - $this->current_column = $error->column; - return false; - } - else - { - return true; - } + return null; } } - function get_error_code() - { - return $this->error_code; - } - - function get_error_string() - { - return $this->error_string; - } - - function get_current_line() - { - return $this->current_line; - } - - function get_current_column() - { - return $this->current_column; - } - - function get_current_byte() - { - return $this->current_byte; - } - - function get_data() + public function get_copyright() { - return $this->data; + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } } - function tag_open($parser, $tag, $attributes) + public function get_language() { - list($this->namespace[], $this->element[]) = $this->split_ns($tag); - - $attribs = array(); - foreach ($attributes as $name => $value) + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) { - list($attrib_namespace, $attribute) = $this->split_ns($name); - $attribs[$attrib_namespace][$attribute] = $value; + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) { - $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)); - $this->xml_base_explicit[] = true; + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - else + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) { - $this->xml_base[] = end($this->xml_base); - $this->xml_base_explicit[] = end($this->xml_base_explicit); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) + elseif (isset($this->data['xml_lang'])) { - $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; + return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); } else { - $this->xml_lang[] = end($this->xml_lang); + return null; } + } - if ($this->current_xhtml_construct >= 0) + public function get_latitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) { - $this->current_xhtml_construct++; - if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML) - { - $this->data['data'] .= '<' . end($this->element); - if (isset($attribs[''])) - { - foreach ($attribs[''] as $name => $value) - { - $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; - } - } - $this->data['data'] .= '>'; - } + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[1]; } else { - $this->datas[] =& $this->data; - $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; - $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); - if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml') - || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')) - { - $this->current_xhtml_construct = 0; - } + return null; } } - function cdata($parser, $cdata) + public function get_longitude() { - if ($this->current_xhtml_construct >= 0) + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) { - $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); + return (float) $return[0]['data']; + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[2]; } else { - $this->data['data'] .= $cdata; + return null; } } - function tag_close($parser, $tag) + public function get_image_url() { - if ($this->current_xhtml_construct >= 0) + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) { - $this->current_xhtml_construct--; - if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) - { - $this->data['data'] .= '</' . end($this->element) . '>'; - } + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); } - if ($this->current_xhtml_construct === -1) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) { - $this->data =& $this->datas[count($this->datas) - 1]; - array_pop($this->datas); + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } - - array_pop($this->element); - array_pop($this->namespace); - array_pop($this->xml_base); - array_pop($this->xml_base_explicit); - array_pop($this->xml_lang); - } - - function split_ns($string) - { - static $cache = array(); - if (!isset($cache[$string])) + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) { - if ($pos = strpos($string, $this->separator)) - { - static $separator_length; - if (!$separator_length) - { - $separator_length = strlen($this->separator); - } - $namespace = substr($string, 0, $pos); - $local_name = substr($string, $pos + $separator_length); - if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) - { - $namespace = SIMPLEPIE_NAMESPACE_ITUNES; - } - - // Normalize the Media RSS namespaces - if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG) - { - $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; - } - $cache[$string] = array($namespace, $local_name); - } - else - { - $cache[$string] = array('', $string); - } + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; } - return $cache[$string]; } } /** - * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags + * Parses the XML Declaration + * + * @package SimplePie + * @subpackage Parsing */ -class SimplePie_Sanitize +class SimplePie_XML_Declaration_Parser { - // Private vars - var $base; + /** + * XML Version + * + * @access public + * @var string + */ + var $version = '1.0'; - // Options - var $remove_div = true; - var $image_handler = ''; - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); - var $encode_instead_of_strip = false; - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); - var $strip_comments = false; - var $output_encoding = 'UTF-8'; - var $enable_cache = true; - var $cache_location = './cache'; - var $cache_name_function = 'md5'; - var $cache_class = 'SimplePie_Cache'; - var $file_class = 'SimplePie_File'; - var $timeout = 10; - var $useragent = ''; - var $force_fsockopen = false; + /** + * Encoding + * + * @access public + * @var string + */ + var $encoding = 'UTF-8'; - var $replace_url_attributes = array( - 'a' => 'href', - 'area' => 'href', - 'blockquote' => 'cite', - 'del' => 'cite', - 'form' => 'action', - 'img' => array('longdesc', 'src'), - 'input' => 'src', - 'ins' => 'cite', - 'q' => 'cite' - ); + /** + * Standalone + * + * @access public + * @var bool + */ + var $standalone = false; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'before_version_name'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; - function remove_div($enable = true) + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + public function __construct($data) { - $this->remove_div = (bool) $enable; + $this->data = $data; + $this->data_length = strlen($this->data); } - function set_image_handler($page = false) + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + public function parse() { - if ($page) + while ($this->state && $this->state !== 'emit' && $this->has_data()) { - $this->image_handler = (string) $page; + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit') + { + return true; } else { - $this->image_handler = false; + $this->version = ''; + $this->encoding = ''; + $this->standalone = ''; + return false; } } - function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + public function has_data() { - if (isset($enable_cache)) - { - $this->enable_cache = (bool) $enable_cache; - } + return (bool) ($this->position < $this->data_length); + } - if ($cache_location) + /** + * Advance past any whitespace + * + * @return int Number of whitespace characters passed + */ + public function skip_whitespace() + { + $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); + $this->position += $whitespace; + return $whitespace; + } + + /** + * Read value + */ + public function get_value() + { + $quote = substr($this->data, $this->position, 1); + if ($quote === '"' || $quote === "'") { - $this->cache_location = (string) $cache_location; + $this->position++; + $len = strcspn($this->data, $quote, $this->position); + if ($this->has_data()) + { + $value = substr($this->data, $this->position, $len); + $this->position += $len + 1; + return $value; + } } + return false; + } - if ($cache_name_function) + public function before_version_name() + { + if ($this->skip_whitespace()) { - $this->cache_name_function = (string) $cache_name_function; + $this->state = 'version_name'; } - - if ($cache_class) + else { - $this->cache_class = (string) $cache_class; + $this->state = false; } } - function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) + public function version_name() { - if ($file_class) + if (substr($this->data, $this->position, 7) === 'version') { - $this->file_class = (string) $file_class; + $this->position += 7; + $this->skip_whitespace(); + $this->state = 'version_equals'; } - - if ($timeout) + else { - $this->timeout = (string) $timeout; + $this->state = false; } + } - if ($useragent) + public function version_equals() + { + if (substr($this->data, $this->position, 1) === '=') { - $this->useragent = (string) $useragent; + $this->position++; + $this->skip_whitespace(); + $this->state = 'version_value'; } - - if ($force_fsockopen) + else { - $this->force_fsockopen = (string) $force_fsockopen; + $this->state = false; } } - function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) + public function version_value() { - if ($tags) + if ($this->version = $this->get_value()) { - if (is_array($tags)) + $this->skip_whitespace(); + if ($this->has_data()) { - $this->strip_htmltags = $tags; + $this->state = 'encoding_name'; } else { - $this->strip_htmltags = explode(',', $tags); + $this->state = 'emit'; } } else { - $this->strip_htmltags = false; + $this->state = false; } } - function encode_instead_of_strip($encode = false) + public function encoding_name() { - $this->encode_instead_of_strip = (bool) $encode; + if (substr($this->data, $this->position, 8) === 'encoding') + { + $this->position += 8; + $this->skip_whitespace(); + $this->state = 'encoding_equals'; + } + else + { + $this->state = 'standalone_name'; + } } - function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) + public function encoding_equals() { - if ($attribs) + if (substr($this->data, $this->position, 1) === '=') { - if (is_array($attribs)) + $this->position++; + $this->skip_whitespace(); + $this->state = 'encoding_value'; + } + else + { + $this->state = false; + } + } + + public function encoding_value() + { + if ($this->encoding = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) { - $this->strip_attributes = $attribs; + $this->state = 'standalone_name'; } else { - $this->strip_attributes = explode(',', $attribs); + $this->state = 'emit'; } } else { - $this->strip_attributes = false; + $this->state = false; } } - function strip_comments($strip = false) - { - $this->strip_comments = (bool) $strip; - } - - function set_output_encoding($encoding = 'UTF-8') - { - $this->output_encoding = (string) $encoding; - } - - /** - * Set element/attribute key/value pairs of HTML attributes - * containing URLs that need to be resolved relative to the feed - * - * @access public - * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs - */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + public function standalone_name() { - $this->replace_url_attributes = (array) $element_attribute; + if (substr($this->data, $this->position, 10) === 'standalone') + { + $this->position += 10; + $this->skip_whitespace(); + $this->state = 'standalone_equals'; + } + else + { + $this->state = false; + } } - function sanitize($data, $type, $base = '') + public function standalone_equals() { - $data = trim($data); - if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) + if (substr($this->data, $this->position, 1) === '=') { - if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) - { - if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) - { - $type |= SIMPLEPIE_CONSTRUCT_HTML; - } - else - { - $type |= SIMPLEPIE_CONSTRUCT_TEXT; - } - } - - if ($type & SIMPLEPIE_CONSTRUCT_BASE64) - { - $data = base64_decode($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_XHTML) - { - if ($this->remove_div) - { - $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data); - $data = preg_replace('/<\/div>$/', '', $data); - } - else - { - $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data); - } - } - - if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) - { - // Strip comments - if ($this->strip_comments) - { - $data = SimplePie_Misc::strip_comments($data); - } - - // Strip out HTML tags and attributes that might cause various security problems. - // Based on recommendations by Mark Pilgrim at: - // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely - if ($this->strip_htmltags) - { - foreach ($this->strip_htmltags as $tag) - { - $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU'; - while (preg_match($pcre, $data)) - { - $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data); - } - } - } - - if ($this->strip_attributes) - { - foreach ($this->strip_attributes as $attrib) - { - $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data); - } - } - - // Replace relative URLs - $this->base = $base; - foreach ($this->replace_url_attributes as $element => $attributes) - { - $data = $this->replace_urls($data, $element, $attributes); - } - - // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. - if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) - { - $images = SimplePie_Misc::get_element('img', $data); - foreach ($images as $img) - { - if (isset($img['attribs']['src']['data'])) - { - $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi'); - - if ($cache->load()) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - $file = new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - $headers = $file->headers; - - if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - } - } - } - } - } - } - - // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data - $data = trim($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_IRI) - { - $data = SimplePie_Misc::absolutize_url($data, $base); - } - - if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) - { - $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); - } - - if ($this->output_encoding !== 'UTF-8') - { - $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding); - } + $this->position++; + $this->skip_whitespace(); + $this->state = 'standalone_value'; + } + else + { + $this->state = false; } - return $data; } - function replace_urls($data, $tag, $attributes) + public function standalone_value() { - if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) + if ($standalone = $this->get_value()) { - $elements = SimplePie_Misc::get_element($tag, $data); - foreach ($elements as $element) + switch ($standalone) { - if (is_array($attributes)) - { - foreach ($attributes as $attribute) - { - if (isset($element['attribs'][$attribute]['data'])) - { - $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base); - $new_element = SimplePie_Misc::element_implode($element); - $data = str_replace($element['full'], $new_element, $data); - $element['full'] = $new_element; - } - } - } - elseif (isset($element['attribs'][$attributes]['data'])) - { - $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base); - $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data); - } + case 'yes': + $this->standalone = true; + break; + + case 'no': + $this->standalone = false; + break; + + default: + $this->state = false; + return; } - } - return $data; - } - function do_strip_htmltags($match) - { - if ($this->encode_instead_of_strip) - { - if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + $this->skip_whitespace(); + if ($this->has_data()) { - $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); - $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); - return "<$match[1]$match[2]>$match[3]</$match[1]>"; + $this->state = false; } else { - return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); + $this->state = 'emit'; } } - elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) - { - return $match[4]; - } else { - return ''; + $this->state = false; } } } -?> -- cgit v1.2.3