Khắc Phục Vấn Đề Mã Hóa Tên File Tiếng Trung Trong RTorrent
Vài hôm trước, mình vừa cài đặt phần mềm tải torrent trên chiếc LS Pro của mình. Trước đây khi dùng FreeBSD, mình luôn dùng ctorrent, lần này muốn đổi gió nên chọn RTorrent. Phần mềm này được cập nhật thường xuyên, phiên bản mới hỗ trợ DHT khá tốt. Tiếc là kho Debian cung cấp phiên bản quá cũ, còn kho trên trang chủ thì lại không truy cập được. Đành phải tải mã nguồn về tự biên dịch vậy.
Ban đầu tưởng đây là phần mềm nhỏ gọn, biên dịch cục bộ là xong. Ai ngờ tốc độ biên dịch mã C++ chậm kinh khủng. Trên LS Pro, mỗi file .cc ngắn ngủi cũng mất tới 20 giây để biên dịch. Lúc đó mới tiếc nuối vì không dùng biên dịch chéo, nhưng cũng đành cắn răng chịu đựng vài tiếng đồng hồ.
Phiên bản mới hỗ trợ giao thức điều khiển xmlrpc nên mình cài thêm giao diện web để quản lý cho tiện :) Chọn dùng rtgui, cảm thấy khá ổn. Vừa tưởng đã hoàn thành xong xuôi thì thử tải một torrent, kết quả tên file tiếng Trung bị lỗi mã hóa, khiến cả rtgui hoạt động bất thường :( Đành thức trắng đêm để xử lý vấn đề này, vất vả vô cùng.
Theo thông tin tìm được, chỉ cần thêm dòng encoding_list = utf-8
vào file cấu hình là có thể giải quyết. Sau khi thử nghiệm, phần lớn torrent hoạt động bình thường. Tuy nhiên vẫn còn một số trường hợp tên file bị sai.
So sánh hai file torrent, phát hiện các tên file đúng đều có ghi chú “utf-8” ở đầu, còn các tên bị lỗi thì dùng mã hóa GBK mà không khai báo. Mình nghi ngờ RTorrent khi gặp tên file không khai báo mã hóa sẽ giữ nguyên tạo file. Qua đọc mã nguồn xác nhận đúng như vậy. Thật may khi phần mềm mã nguồn mở giúp mình kiểm tra tận gốc rễ. Dù vậy, việc phân tích mã C++ với các lớp kế thừa vòng vo trong RTorrent cũng tốn không ít thời gian, nhất là khi chỉ có mỗi vi và grep để hỗ trợ.
Tìm kiếm trên Google cả buổi mà chẳng thấy ai ở châu Á quan tâm vấn đề này. Qua nghiên cứu mã nguồn, mình khẳng định không thể giải quyết chỉ bằng cấu hình, vì RTorrent hoàn toàn không có hàm chuyển đổi mã hóa nào.
Cuối cùng quyết định tự tay sửa mã nguồn. Ai bảo phần mềm mở là để người dùng tùy ý chỉnh sửa mà. Vì muốn giải quyết nhanh, mình quyết định “cứng hóa” đoạn mã xử lý: khi gặp tên file không khai báo mã hóa thì ép chuyển từ GBK sang UTF-8.
RTorrent được viết bằng C++, cấu trúc mã rất đẹp mắt với các lớp kế thừa rõ ràng. Tuy nhiên cũng có điểm yếu là quá chú trọng trừu tượng hóa, đôi khi khiến mã trở nên rườm rà. 90% mã sạch sẽ, chỉ 10% nhỏ có mùi “khó chịu”, nhưng chính 10% đó lại là phần làm việc thực tế. Qua nghiên cứu này, mình càng thấm thía sức mạnh của C++ và giá trị của lập trình viên giỏi :)
Trở lại vấn đề chính: Sau khi phân tích, mình xác định lớp Path trong libtorrent chịu trách nhiệm xử lý tên file. Qua xem xét file path.h, thấy Path kế thừa riêng tư từ vectoras_string
thu hút sự chú ý vì là phương thức duy nhất lấy chuỗi bên trong. Biến m_encoding
private lưu thông tin mã hóa. Khi encoding trống, mình sẽ chèn đoạn mã xử lý.
Vì biên dịch C++ chậm, mình cố gắng không sửa file .h. Bắt đầu từ path.cc, thêm kiểm tra encoding rỗng rồi dùng iconv chuyển mã. Sửa xong biên dịch lại nhưng vấn đề vẫn chưa khắc phục :( Qua thêm log kiểm tra, xác nhận đoạn mã đã chạy đúng.
Bắt đầu nghi ngờ Path có dùng “đường dây tắt”. Qua nghiên cứu kỹ .h và grep toàn bộ mã nguồn, phát hiện Path cung cấp phương thức begin/end trả về iterator của vector
Tiếp tục phân tích, mình khẳng định khi tạo thư mục, chương trình đã dùng “đường dây tắt” truy cập trực tiếp dữ liệu lớp cơ sở. Do đó đoạn sửa trước đó chưa đủ. Dù rất ngại, mình đành sửa file path.h vì C++ không cho phép thêm phương thức thành viên dễ dàng như C. Việc sửa này khiến hàng chục file .cc phải biên dịch lại (mất hơn nửa tiếng trên LS Pro), dù chúng không dùng đến phương thức mới.
Cuối cùng, mình thêm vào Path một phương thức chuyển đổi chuỗi nội bộ sang utf-8 khi chưa khai báo mã hóa. (Về lý thuyết có thể thiết kế module tự động chuyển đổi bên trong Path, nhưng mình từ bỏ ý định vì mất thời gian). Sửa tiếp file download_constructor.cc
, thêm lời gọi hàm chuyển đổi trong hàm DownloadConstructor::choose_path
. Kết quả: mọi tên file tiếng Trung đều hiển thị đúng utf-8. Hoàn hảo, trừ cái “miếng vá” trong mã nguồn trông rất mất mỹ quan, chẳng giống phong cách C++ chút nào.
Bổ sung ngày 30/1/2009: Mặc dù đã giải thích rõ ràng, vẫn có bạn muốn dùng luôn bản vá. Mình up file patch lên đây, nhưng phải cảnh báo trước là giải pháp này không hề tinh tế, chỉ là cách mình xử lý vội vàng lúc đó. Sau khi áp dụng patch, cần cài iconv mới biên dịch được. [Tải về (libtorrent-0.12.3.patch)] Không cần hỏi chi tiết cách patch hay biên dịch như thế nào nữa nhé!