summaryrefslogtreecommitdiffstats
path: root/kioslave
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kioslave
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kioslave')
-rw-r--r--kioslave/DEBUG.howto89
-rw-r--r--kioslave/DESIGN43
-rw-r--r--kioslave/Makefile.am11
-rw-r--r--kioslave/about/Makefile.am21
-rw-r--r--kioslave/about/about.protocol9
-rw-r--r--kioslave/about/kio_about.cpp76
-rw-r--r--kioslave/about/kio_about.h39
-rw-r--r--kioslave/cgi/Makefile.am16
-rw-r--r--kioslave/cgi/README15
-rw-r--r--kioslave/cgi/cgi.cpp273
-rw-r--r--kioslave/cgi/cgi.h48
-rw-r--r--kioslave/cgi/cgi.protocol8
-rw-r--r--kioslave/cgi/kcmcgi/Makefile.am17
-rw-r--r--kioslave/cgi/kcmcgi/kcmcgi.cpp151
-rw-r--r--kioslave/cgi/kcmcgi/kcmcgi.desktop231
-rw-r--r--kioslave/cgi/kcmcgi/kcmcgi.h55
-rw-r--r--kioslave/configure.in.bot7
-rw-r--r--kioslave/configure.in.in15
-rw-r--r--kioslave/filter/Makefile.am21
-rw-r--r--kioslave/filter/bzip.protocol10
-rw-r--r--kioslave/filter/bzip2.protocol10
-rw-r--r--kioslave/filter/configure.in.in1
-rw-r--r--kioslave/filter/filter.cc167
-rw-r--r--kioslave/filter/filter.h46
-rw-r--r--kioslave/filter/gzip.protocol10
-rw-r--r--kioslave/finger/Makefile.am27
-rw-r--r--kioslave/finger/finger.protocol9
-rw-r--r--kioslave/finger/kio_finger.cpp266
-rw-r--r--kioslave/finger/kio_finger.css69
-rw-r--r--kioslave/finger/kio_finger.h64
-rw-r--r--kioslave/finger/kio_finger.pl175
-rw-r--r--kioslave/fish/AUTHORS1
-rw-r--r--kioslave/fish/COPYING340
-rw-r--r--kioslave/fish/ChangeLog71
-rw-r--r--kioslave/fish/FAQ37
-rw-r--r--kioslave/fish/INSTALL167
-rw-r--r--kioslave/fish/Makefile.am33
-rw-r--r--kioslave/fish/README258
-rw-r--r--kioslave/fish/TODO10
-rw-r--r--kioslave/fish/configure.in.in9
-rw-r--r--kioslave/fish/fish.cpp1661
-rw-r--r--kioslave/fish/fish.h211
-rwxr-xr-xkioslave/fish/fish.pl369
-rw-r--r--kioslave/fish/fish.protocol81
-rw-r--r--kioslave/fish/nxfish.protocol74
-rw-r--r--kioslave/floppy/AUTHORS2
-rw-r--r--kioslave/floppy/Makefile.am20
-rw-r--r--kioslave/floppy/README7
-rw-r--r--kioslave/floppy/TODO3
-rw-r--r--kioslave/floppy/floppy.protocol14
-rw-r--r--kioslave/floppy/kio_floppy.cpp1169
-rw-r--r--kioslave/floppy/kio_floppy.h78
-rw-r--r--kioslave/floppy/program.cpp201
-rw-r--r--kioslave/floppy/program.h53
-rw-r--r--kioslave/home/Makefile.am32
-rw-r--r--kioslave/home/dummy.cpp1
-rw-r--r--kioslave/home/home.protocol19
-rw-r--r--kioslave/home/homeimpl.cpp228
-rw-r--r--kioslave/home/homeimpl.h57
-rw-r--r--kioslave/home/kdedmodule/Makefile.am13
-rw-r--r--kioslave/home/kdedmodule/homedirnotify.cpp185
-rw-r--r--kioslave/home/kdedmodule/homedirnotify.desktop60
-rw-r--r--kioslave/home/kdedmodule/homedirnotify.h48
-rw-r--r--kioslave/home/kdedmodule/homedirnotifymodule.cpp37
-rw-r--r--kioslave/home/kdedmodule/homedirnotifymodule.h36
-rw-r--r--kioslave/home/kio_home.cpp186
-rw-r--r--kioslave/home/kio_home.h44
-rw-r--r--kioslave/home/testhome.cpp69
-rw-r--r--kioslave/home/testhome.h34
-rw-r--r--kioslave/info/LICENSE22
-rw-r--r--kioslave/info/Makefile.am21
-rw-r--r--kioslave/info/info.cc261
-rw-r--r--kioslave/info/info.h36
-rw-r--r--kioslave/info/info.protocol11
-rwxr-xr-xkioslave/info/kde-info2html1031
-rw-r--r--kioslave/info/kde-info2html.conf43
-rw-r--r--kioslave/ldap/LICENSE16
-rw-r--r--kioslave/ldap/Makefile.am22
-rw-r--r--kioslave/ldap/configure.in.in111
-rw-r--r--kioslave/ldap/kio_ldap.cpp1154
-rw-r--r--kioslave/ldap/kio_ldap.h65
-rw-r--r--kioslave/ldap/ldap.protocol17
-rw-r--r--kioslave/ldap/ldaps.protocol17
-rw-r--r--kioslave/mac/AUTHORS1
-rw-r--r--kioslave/mac/ChangeLog39
-rw-r--r--kioslave/mac/Makefile.am23
-rw-r--r--kioslave/mac/README65
-rw-r--r--kioslave/mac/TODO14
-rw-r--r--kioslave/mac/cr16-app-mac.pngbin0 -> 679 bytes
-rw-r--r--kioslave/mac/cr32-app-mac.pngbin0 -> 1342 bytes
-rw-r--r--kioslave/mac/kio_mac.cpp561
-rw-r--r--kioslave/mac/kio_mac.h55
-rw-r--r--kioslave/mac/mac.protocol73
-rw-r--r--kioslave/man/LICENSE16
-rw-r--r--kioslave/man/Makefile.am51
-rw-r--r--kioslave/man/kio_man.cpp1532
-rw-r--r--kioslave/man/kio_man.css21
-rw-r--r--kioslave/man/kio_man.h99
-rw-r--r--kioslave/man/kio_man_test.cpp38
-rw-r--r--kioslave/man/kmanpart.cpp115
-rw-r--r--kioslave/man/kmanpart.desktop91
-rw-r--r--kioslave/man/kmanpart.h79
-rw-r--r--kioslave/man/man.protocol12
-rw-r--r--kioslave/man/man2html.cpp5683
-rw-r--r--kioslave/man/man2html.h34
-rw-r--r--kioslave/media/Makefile.am36
-rw-r--r--kioslave/media/configure.in.in184
-rw-r--r--kioslave/media/contrib/README18
-rwxr-xr-xkioslave/media/contrib/mediamanager_usbstorage.dev108
-rwxr-xr-xkioslave/media/contrib/usbcam82
-rw-r--r--kioslave/media/dummy.cpp1
-rw-r--r--kioslave/media/kcmodule/Makefile.am21
-rw-r--r--kioslave/media/kcmodule/main.cpp103
-rw-r--r--kioslave/media/kcmodule/main.h45
-rw-r--r--kioslave/media/kcmodule/managermodule.cpp74
-rw-r--r--kioslave/media/kcmodule/managermodule.h34
-rw-r--r--kioslave/media/kcmodule/managermoduleview.ui72
-rw-r--r--kioslave/media/kcmodule/media.desktop197
-rw-r--r--kioslave/media/kcmodule/mimetypelistboxitem.cpp35
-rw-r--r--kioslave/media/kcmodule/mimetypelistboxitem.h37
-rw-r--r--kioslave/media/kcmodule/notifiermodule.cpp230
-rw-r--r--kioslave/media/kcmodule/notifiermodule.h57
-rw-r--r--kioslave/media/kcmodule/notifiermoduleview.ui171
-rw-r--r--kioslave/media/kcmodule/serviceconfigdialog.cpp151
-rw-r--r--kioslave/media/kcmodule/serviceconfigdialog.h48
-rw-r--r--kioslave/media/kcmodule/serviceview.ui248
-rw-r--r--kioslave/media/kfile-plugin/Makefile.am13
-rw-r--r--kioslave/media/kfile-plugin/kfile_media.desktop75
-rw-r--r--kioslave/media/kfile-plugin/kfilemediaplugin.cpp197
-rw-r--r--kioslave/media/kfile-plugin/kfilemediaplugin.h47
-rw-r--r--kioslave/media/kio_media.cpp275
-rw-r--r--kioslave/media/kio_media.h54
-rw-r--r--kioslave/media/libmediacommon/Makefile.am11
-rw-r--r--kioslave/media/libmediacommon/actionlistboxitem.cpp47
-rw-r--r--kioslave/media/libmediacommon/actionlistboxitem.h40
-rw-r--r--kioslave/media/libmediacommon/mediamanagersettings.kcfg23
-rw-r--r--kioslave/media/libmediacommon/mediamanagersettings.kcfgc4
-rw-r--r--kioslave/media/libmediacommon/medium.cpp228
-rw-r--r--kioslave/media/libmediacommon/medium.h110
-rw-r--r--kioslave/media/libmediacommon/notifieraction.cpp97
-rw-r--r--kioslave/media/libmediacommon/notifieraction.h60
-rw-r--r--kioslave/media/libmediacommon/notifiernothingaction.cpp39
-rw-r--r--kioslave/media/libmediacommon/notifiernothingaction.h34
-rw-r--r--kioslave/media/libmediacommon/notifieropenaction.cpp45
-rw-r--r--kioslave/media/libmediacommon/notifieropenaction.h34
-rw-r--r--kioslave/media/libmediacommon/notifierserviceaction.cpp166
-rw-r--r--kioslave/media/libmediacommon/notifierserviceaction.h61
-rw-r--r--kioslave/media/libmediacommon/notifiersettings.cpp379
-rw-r--r--kioslave/media/libmediacommon/notifiersettings.h63
-rw-r--r--kioslave/media/media.protocol18
-rw-r--r--kioslave/media/mediaimpl.cpp429
-rw-r--r--kioslave/media/mediaimpl.h81
-rw-r--r--kioslave/media/mediamanager/Makefile.am32
-rw-r--r--kioslave/media/mediamanager/backendbase.cpp26
-rw-r--r--kioslave/media/mediamanager/backendbase.h35
-rw-r--r--kioslave/media/mediamanager/fstabbackend.cpp483
-rw-r--r--kioslave/media/mediamanager/fstabbackend.h68
-rw-r--r--kioslave/media/mediamanager/halbackend.cpp1345
-rw-r--r--kioslave/media/mediamanager/halbackend.h228
-rw-r--r--kioslave/media/mediamanager/linuxcdpolling.cpp585
-rw-r--r--kioslave/media/mediamanager/linuxcdpolling.h86
-rw-r--r--kioslave/media/mediamanager/mediadirnotify.cpp124
-rw-r--r--kioslave/media/mediamanager/mediadirnotify.h47
-rw-r--r--kioslave/media/mediamanager/medialist.cpp288
-rw-r--r--kioslave/media/mediamanager/medialist.h79
-rw-r--r--kioslave/media/mediamanager/mediamanager.cpp342
-rw-r--r--kioslave/media/mediamanager/mediamanager.desktop141
-rw-r--r--kioslave/media/mediamanager/mediamanager.h90
-rw-r--r--kioslave/media/mediamanager/removablebackend.cpp180
-rw-r--r--kioslave/media/mediamanager/removablebackend.h52
-rw-r--r--kioslave/media/medianotifier/Makefile.am18
-rw-r--r--kioslave/media/medianotifier/medianotifier.cpp312
-rw-r--r--kioslave/media/medianotifier/medianotifier.desktop122
-rw-r--r--kioslave/media/medianotifier/medianotifier.h57
-rw-r--r--kioslave/media/medianotifier/notificationdialog.cpp147
-rw-r--r--kioslave/media/medianotifier/notificationdialog.h56
-rw-r--r--kioslave/media/medianotifier/notificationdialogview.ui117
-rw-r--r--kioslave/media/mimetypes/Makefile.am22
-rw-r--r--kioslave/media/mimetypes/audiocd.desktop66
-rw-r--r--kioslave/media/mimetypes/blankcd.desktop73
-rw-r--r--kioslave/media/mimetypes/blankdvd.desktop73
-rw-r--r--kioslave/media/mimetypes/camera_mounted.desktop69
-rw-r--r--kioslave/media/mimetypes/camera_unmounted.desktop69
-rw-r--r--kioslave/media/mimetypes/cdrom_mounted.desktop85
-rw-r--r--kioslave/media/mimetypes/cdrom_unmounted.desktop86
-rw-r--r--kioslave/media/mimetypes/cdwriter_mounted.desktop86
-rw-r--r--kioslave/media/mimetypes/cdwriter_unmounted.desktop86
-rw-r--r--kioslave/media/mimetypes/dvd_mounted.desktop81
-rw-r--r--kioslave/media/mimetypes/dvd_unmounted.desktop81
-rw-r--r--kioslave/media/mimetypes/dvdvideo.desktop73
-rw-r--r--kioslave/media/mimetypes/floppy5_mounted.desktop85
-rw-r--r--kioslave/media/mimetypes/floppy5_unmounted.desktop85
-rw-r--r--kioslave/media/mimetypes/floppy_mounted.desktop85
-rw-r--r--kioslave/media/mimetypes/floppy_unmounted.desktop85
-rw-r--r--kioslave/media/mimetypes/gphoto2camera.desktop76
-rw-r--r--kioslave/media/mimetypes/hdd_mounted.desktop77
-rw-r--r--kioslave/media/mimetypes/hdd_unmounted.desktop77
-rw-r--r--kioslave/media/mimetypes/nfs_mounted.desktop77
-rw-r--r--kioslave/media/mimetypes/nfs_unmounted.desktop77
-rw-r--r--kioslave/media/mimetypes/removable_mounted.desktop77
-rw-r--r--kioslave/media/mimetypes/removable_unmounted.desktop77
-rw-r--r--kioslave/media/mimetypes/smb_mounted.desktop83
-rw-r--r--kioslave/media/mimetypes/smb_unmounted.desktop84
-rw-r--r--kioslave/media/mimetypes/svcd.desktop50
-rw-r--r--kioslave/media/mimetypes/vcd.desktop50
-rw-r--r--kioslave/media/mimetypes/zip_mounted.desktop85
-rw-r--r--kioslave/media/mimetypes/zip_unmounted.desktop85
-rw-r--r--kioslave/media/mounthelper/Makefile.am12
-rw-r--r--kioslave/media/mounthelper/kio_media_mounthelper.cpp209
-rw-r--r--kioslave/media/mounthelper/kio_media_mounthelper.h49
-rw-r--r--kioslave/media/propsdlgplugin/Makefile.am12
-rw-r--r--kioslave/media/propsdlgplugin/media_propsdlgplugin.desktop102
-rw-r--r--kioslave/media/propsdlgplugin/propertiespage.cpp217
-rw-r--r--kioslave/media/propsdlgplugin/propertiespage.h46
-rw-r--r--kioslave/media/propsdlgplugin/propertiespagegui.ui387
-rw-r--r--kioslave/media/propsdlgplugin/propertiespagegui.ui.h22
-rw-r--r--kioslave/media/propsdlgplugin/propsdlgshareplugin.cpp100
-rw-r--r--kioslave/media/propsdlgplugin/propsdlgshareplugin.h44
-rw-r--r--kioslave/media/services/Makefile.am3
-rw-r--r--kioslave/media/services/media_eject.desktop90
-rw-r--r--kioslave/media/services/media_mount.desktop87
-rw-r--r--kioslave/media/services/media_safelyremove.desktop75
-rw-r--r--kioslave/media/services/media_unmount.desktop87
-rw-r--r--kioslave/media/testmedia.cpp69
-rw-r--r--kioslave/media/testmedia.h34
-rw-r--r--kioslave/nfs/AUTHORS2
-rw-r--r--kioslave/nfs/Makefile.am28
-rw-r--r--kioslave/nfs/README3
-rw-r--r--kioslave/nfs/TODO7
-rw-r--r--kioslave/nfs/kio_nfs.cpp1615
-rw-r--r--kioslave/nfs/kio_nfs.h109
-rw-r--r--kioslave/nfs/mount.h325
-rw-r--r--kioslave/nfs/mount.x255
-rw-r--r--kioslave/nfs/mount_xdr.c335
-rw-r--r--kioslave/nfs/nfs.protocol14
-rw-r--r--kioslave/nfs/nfs_prot.h699
-rw-r--r--kioslave/nfs/nfs_prot.x365
-rw-r--r--kioslave/nfs/nfs_prot_xdr.c886
-rw-r--r--kioslave/nntp/LICENSE16
-rw-r--r--kioslave/nntp/Makefile.am19
-rw-r--r--kioslave/nntp/nntp.cpp896
-rw-r--r--kioslave/nntp/nntp.h130
-rw-r--r--kioslave/nntp/nntp.protocol11
-rw-r--r--kioslave/nntp/nntps.protocol11
-rw-r--r--kioslave/pop3/Makefile.am17
-rw-r--r--kioslave/pop3/pop3.cc1263
-rw-r--r--kioslave/pop3/pop3.h133
-rw-r--r--kioslave/pop3/pop3.protocol16
-rw-r--r--kioslave/pop3/pop3s.protocol16
-rw-r--r--kioslave/remote/Makefile.am32
-rw-r--r--kioslave/remote/dummy.cpp1
-rw-r--r--kioslave/remote/kdedmodule/Makefile.am13
-rw-r--r--kioslave/remote/kdedmodule/remotedirnotify.cpp143
-rw-r--r--kioslave/remote/kdedmodule/remotedirnotify.desktop64
-rw-r--r--kioslave/remote/kdedmodule/remotedirnotify.h43
-rw-r--r--kioslave/remote/kdedmodule/remotedirnotifymodule.cpp37
-rw-r--r--kioslave/remote/kdedmodule/remotedirnotifymodule.h36
-rw-r--r--kioslave/remote/kio_remote.cpp234
-rw-r--r--kioslave/remote/kio_remote.h45
-rw-r--r--kioslave/remote/remote.protocol17
-rw-r--r--kioslave/remote/remoteimpl.cpp298
-rw-r--r--kioslave/remote/remoteimpl.h54
-rw-r--r--kioslave/remote/testremote.cpp69
-rw-r--r--kioslave/remote/testremote.h34
-rw-r--r--kioslave/settings/Makefile.am21
-rw-r--r--kioslave/settings/applications.protocol15
-rw-r--r--kioslave/settings/kio_settings.cc298
-rw-r--r--kioslave/settings/kio_settings.h0
-rw-r--r--kioslave/settings/programs.protocol15
-rw-r--r--kioslave/settings/settings.protocol15
-rw-r--r--kioslave/sftp/AUTHORS3
-rw-r--r--kioslave/sftp/CHANGELOG59
-rw-r--r--kioslave/sftp/DEBUGGING12
-rw-r--r--kioslave/sftp/Makefile.am25
-rw-r--r--kioslave/sftp/TODO5
-rw-r--r--kioslave/sftp/atomicio.cpp67
-rw-r--r--kioslave/sftp/atomicio.h39
-rw-r--r--kioslave/sftp/kio_sftp.cpp2286
-rw-r--r--kioslave/sftp/kio_sftp.h149
-rw-r--r--kioslave/sftp/ksshprocess.cpp1104
-rw-r--r--kioslave/sftp/ksshprocess.h623
-rw-r--r--kioslave/sftp/ksshprocesstest.cpp98
-rw-r--r--kioslave/sftp/process.cpp493
-rw-r--r--kioslave/sftp/process.h148
-rw-r--r--kioslave/sftp/sftp.h91
-rw-r--r--kioslave/sftp/sftp.protocol84
-rw-r--r--kioslave/sftp/sftpfileattr.cpp346
-rw-r--r--kioslave/sftp/sftpfileattr.h261
-rw-r--r--kioslave/smb/Makefile.am37
-rw-r--r--kioslave/smb/configure.in.bot9
-rw-r--r--kioslave/smb/configure.in.in34
-rw-r--r--kioslave/smb/kio_smb.cpp77
-rw-r--r--kioslave/smb/kio_smb.h301
-rw-r--r--kioslave/smb/kio_smb_auth.cpp206
-rw-r--r--kioslave/smb/kio_smb_browse.cpp476
-rw-r--r--kioslave/smb/kio_smb_config.cpp66
-rw-r--r--kioslave/smb/kio_smb_dir.cpp345
-rw-r--r--kioslave/smb/kio_smb_file.cpp279
-rw-r--r--kioslave/smb/kio_smb_internal.cpp135
-rw-r--r--kioslave/smb/kio_smb_internal.h118
-rw-r--r--kioslave/smb/kio_smb_mount.cpp211
-rw-r--r--kioslave/smb/libsmbclient-HOWTO.txt11
-rw-r--r--kioslave/smb/smb-network.desktop80
-rw-r--r--kioslave/smb/smb.protocol12
-rw-r--r--kioslave/smb/x-smb-server.desktop76
-rw-r--r--kioslave/smb/x-smb-workgroup.desktop74
-rw-r--r--kioslave/smtp/Makefile.am37
-rw-r--r--kioslave/smtp/TODO11
-rw-r--r--kioslave/smtp/capabilities.cc143
-rw-r--r--kioslave/smtp/capabilities.h77
-rw-r--r--kioslave/smtp/command.cc606
-rw-r--r--kioslave/smtp/command.h283
-rw-r--r--kioslave/smtp/compliance.txt33
-rw-r--r--kioslave/smtp/interactivesmtpserver.cc127
-rw-r--r--kioslave/smtp/interactivesmtpserver.h121
-rw-r--r--kioslave/smtp/request.cc189
-rw-r--r--kioslave/smtp/request.h100
-rw-r--r--kioslave/smtp/response.cc160
-rw-r--r--kioslave/smtp/response.h125
-rw-r--r--kioslave/smtp/smtp.cc647
-rw-r--r--kioslave/smtp/smtp.h146
-rw-r--r--kioslave/smtp/smtp.protocol16
-rw-r--r--kioslave/smtp/smtps.protocol15
-rw-r--r--kioslave/smtp/test_commands.cc728
-rw-r--r--kioslave/smtp/test_headergeneration.cc86
-rw-r--r--kioslave/smtp/test_responseparser.cc107
-rw-r--r--kioslave/smtp/transactionstate.cc114
-rw-r--r--kioslave/smtp/transactionstate.h185
-rw-r--r--kioslave/system/Makefile.am31
-rw-r--r--kioslave/system/dummy.cpp1
-rw-r--r--kioslave/system/entries/Makefile.am5
-rw-r--r--kioslave/system/entries/documents.desktop68
-rw-r--r--kioslave/system/entries/home.desktop78
-rwxr-xr-xkioslave/system/entries/kio_system_documenthelper14
-rw-r--r--kioslave/system/entries/media.desktop71
-rw-r--r--kioslave/system/entries/remote.desktop73
-rw-r--r--kioslave/system/entries/trash.desktop84
-rw-r--r--kioslave/system/entries/users.desktop67
-rw-r--r--kioslave/system/kdedmodule/Makefile.am13
-rw-r--r--kioslave/system/kdedmodule/systemdirnotify.cpp184
-rw-r--r--kioslave/system/kdedmodule/systemdirnotify.desktop64
-rw-r--r--kioslave/system/kdedmodule/systemdirnotify.h47
-rw-r--r--kioslave/system/kdedmodule/systemdirnotifymodule.cpp37
-rw-r--r--kioslave/system/kdedmodule/systemdirnotifymodule.h36
-rw-r--r--kioslave/system/kio_system.cpp189
-rw-r--r--kioslave/system/kio_system.h45
-rw-r--r--kioslave/system/mimetypes/Makefile.am6
-rw-r--r--kioslave/system/mimetypes/system_directory.desktop76
-rw-r--r--kioslave/system/system.protocol19
-rw-r--r--kioslave/system/systemimpl.cpp276
-rw-r--r--kioslave/system/systemimpl.h65
-rw-r--r--kioslave/system/testsystem.cpp69
-rw-r--r--kioslave/system/testsystem.h34
-rw-r--r--kioslave/tar/Makefile.am24
-rw-r--r--kioslave/tar/ar.protocol12
-rw-r--r--kioslave/tar/ktartest.cpp201
-rw-r--r--kioslave/tar/tar.cc623
-rw-r--r--kioslave/tar/tar.h55
-rw-r--r--kioslave/tar/tar.protocol12
-rw-r--r--kioslave/tar/zip.protocol12
-rw-r--r--kioslave/thumbnail/Makefile.am69
-rw-r--r--kioslave/thumbnail/configure.in.in14
-rw-r--r--kioslave/thumbnail/cursorcreator.cpp69
-rw-r--r--kioslave/thumbnail/cursorcreator.h33
-rw-r--r--kioslave/thumbnail/cursorthumbnail.desktop80
-rw-r--r--kioslave/thumbnail/djvucreator.cpp139
-rw-r--r--kioslave/thumbnail/djvucreator.h35
-rw-r--r--kioslave/thumbnail/djvuthumbnail.desktop81
-rw-r--r--kioslave/thumbnail/exrcreator.cpp85
-rw-r--r--kioslave/thumbnail/exrcreator.h33
-rw-r--r--kioslave/thumbnail/exrthumbnail.desktop79
-rw-r--r--kioslave/thumbnail/htmlcreator.cpp118
-rw-r--r--kioslave/thumbnail/htmlcreator.h47
-rw-r--r--kioslave/thumbnail/htmlthumbnail.desktop86
-rw-r--r--kioslave/thumbnail/imagecreator.cpp51
-rw-r--r--kioslave/thumbnail/imagecreator.h33
-rw-r--r--kioslave/thumbnail/imagethumbnail.desktop85
-rw-r--r--kioslave/thumbnail/textcreator.cpp195
-rw-r--r--kioslave/thumbnail/textcreator.h43
-rw-r--r--kioslave/thumbnail/textthumbnail.desktop87
-rw-r--r--kioslave/thumbnail/thumbcreator.desktop86
-rw-r--r--kioslave/thumbnail/thumbnail.cpp440
-rw-r--r--kioslave/thumbnail/thumbnail.h54
-rw-r--r--kioslave/thumbnail/thumbnail.protocol9
-rw-r--r--kioslave/trash/DESIGN53
-rw-r--r--kioslave/trash/Makefile.am31
-rw-r--r--kioslave/trash/kfile-plugin/Makefile.am14
-rw-r--r--kioslave/trash/kfile-plugin/RETURNED_ITEMS4
-rw-r--r--kioslave/trash/kfile-plugin/kfile_trash.cpp93
-rw-r--r--kioslave/trash/kfile-plugin/kfile_trash.desktop79
-rw-r--r--kioslave/trash/kfile-plugin/kfile_trash.h42
-rw-r--r--kioslave/trash/kfile-plugin/kfile_trash_system.desktop79
-rw-r--r--kioslave/trash/kio_trash.cpp596
-rw-r--r--kioslave/trash/kio_trash.h71
-rw-r--r--kioslave/trash/ktrash.cpp102
-rw-r--r--kioslave/trash/testtrash.cpp1198
-rw-r--r--kioslave/trash/testtrash.h118
-rw-r--r--kioslave/trash/trash.protocol89
-rw-r--r--kioslave/trash/trashimpl.cpp962
-rw-r--r--kioslave/trash/trashimpl.h182
400 files changed, 62404 insertions, 0 deletions
diff --git a/kioslave/DEBUG.howto b/kioslave/DEBUG.howto
new file mode 100644
index 000000000..5275a26eb
--- /dev/null
+++ b/kioslave/DEBUG.howto
@@ -0,0 +1,89 @@
+This document describes how you can debug an io-slave with gdb.
+
+How does an io-slave get started?
+=================================
+
+Your application request 'klauncher' via DCOP for a slave. If 'klauncher' does
+not have an idle slave ready, it will ask kdeinit to start a new one.
+kdeinit forks and dlopens the library that contains the io-slave.
+Then it calls kdemain() or, if that is not present, main() in the library.
+
+
+Attaching gdb to a io-slave
+===========================
+
+Due to the above sequence it is rather hard to get an io-slave in your
+debugger. But wait there is hope. You can start klauncher in such a way
+that slaves for a certain protocol are started in debug mode.
+
+E.g. to start all 'http' slaves in debug mode, you type:
+
+ KDE_SLAVE_DEBUG_WAIT=http kdeinit
+
+This will restart 'kdeinit' and 'klauncher'.
+
+When your application now requests a http slave, the slave will be started
+by kdeinit, but before it calls kdemain() (cq. main()) it will suspend the
+slave by sending it a SIGSTOP signal.
+
+In the terminal from which you started kdeinit you will get the following
+message:
+
+kdeinit: Suspending process
+kdeinit: 'gdb kdeinit 16779' to debug
+kdeinit: 'kill -SIGCONT 16779' to continue
+
+You can now debug your slave by typing (or pasting) 'gdb kdeinit 16779' in
+a terminal. If you don't want to debug a slave you can let it continue by
+sending it a SIGCONT by typing 'kill -SIGCONT 16779'.
+
+Be aware that slaves will not be killed while they are suspended.
+
+Once you have started gdb, you can set e.g. breakpoints and then resume the
+slave by typing 'continue'. The debugger will return immediate with a message
+that a SIGSTOP has been received so you will have to type 'continue' a second
+time.
+
+
+Debugging io-slaves with valgrind
+=================================
+
+KLauncher can be told to run certain io-slaves through valgrind. The following
+command can be used to let klauncher run all https io-slaves via valgrind:
+
+ KDE_SLAVE_VALGRIND=https kdeinit
+
+The valgrind output will appear as the stderr output of the kdeinit process.
+The $VALGRIND_OPTS environment variable can be used to pass options to valgrind.
+If you want to use a different skin:
+
+ KDE_SLAVE_VALGRIND_SKIN=calltree ( for example )
+
+
+How to get debug output
+=======================
+
+It is useful to redirect the debug output of your particular slave to a file
+instead of stderr. E.g. I myself use the following lines in
+$KDEDIR/share/config/kdebugrc.
+
+ [7113]
+ InfoOutput=0
+ InfoFilename=/tmp/http
+ [7103]
+ InfoOutput=0
+ InfoFilename=/tmp/http
+
+This redirects all debug info for areas 7103 and 7113 (as used by kio_http)
+to the file /tmp/http.
+
+To get debug information from the SMB slave you can add the following to
+kioslaverc:
+
+[SMB]
+DebugLevel=100
+
+This will print additional debug info to the stderr of your kdeinit process,
+which typically ends up in ~/.X.err or ~/.xsession-errors
+
+Happy debugging.
diff --git a/kioslave/DESIGN b/kioslave/DESIGN
new file mode 100644
index 000000000..92a03b212
--- /dev/null
+++ b/kioslave/DESIGN
@@ -0,0 +1,43 @@
+What is a kioslave you ask yourself?
+
+A kioslave is a program designed to be intimately familiar with a certian
+protocol, so that a standardized interface can be used to get at data from
+any number of places. A few examples are the http and ftp kioslaves,
+which using nearly identical methods will retrieve data from an http or
+ftp server respectively.
+
+Well, that's nice. How do they work?
+
+To understand it, you'll need two ice cubes, a pair of handcuffs, and a
+ferret. Some Crisco (or other shortening) is optional. Well, that aside,
+this document focuses on the business end of the whole kio library. The
+ioslave. See the documentation of the SlaveBase class for the methods
+you need to reimplement, and see
+http://developer.kde.org/documentation/design/kde/ioslaves/ for more docu
+online.
+
+That's nice, but how can I use it?
+
+Any time you'd like to use non blocking IO over a high level protocol
+(such as HTTP or FTP) a kioslave is for you.
+
+That's nice, but how do I use it?
+
+Basically, you create "jobs" by calling a public KIO::blah method
+(the correct prototypes, etc, are in kio/job.h). Once this is done, you
+connect to the result() signal, and wait for the result. There are
+other signals emitted by jobs, see kio/jobclasses.h. Once again,
+see the online documentation for more.
+
+
+If you are interested in working on an ioslave,
+the following slaves are severely lacking in functionality:
+
+ SMTP
+ SMB
+
+-------------
+
+Original document by Rich.
+Updated for KDE 2 by David.
+
diff --git a/kioslave/Makefile.am b/kioslave/Makefile.am
new file mode 100644
index 000000000..67e21cd87
--- /dev/null
+++ b/kioslave/Makefile.am
@@ -0,0 +1,11 @@
+if include_kioslave_ldap
+LDAP_SUBDIR=ldap
+endif
+
+if include_kioslave_smb
+SMB_SUBDIR=smb
+endif
+
+SUBDIRS = about cgi floppy filter fish info mac man nfs nntp pop3 smtp \
+ sftp tar finger thumbnail $(LDAP_SUBDIR) $(SMB_SUBDIR) settings trash media \
+ remote home system
diff --git a/kioslave/about/Makefile.am b/kioslave/about/Makefile.am
new file mode 100644
index 000000000..9f0e959e1
--- /dev/null
+++ b/kioslave/about/Makefile.am
@@ -0,0 +1,21 @@
+## Makefile.am of kdebase/kioslave/about
+
+INCLUDES= $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_about.la
+
+kio_about_la_SOURCES = kio_about.cpp
+kio_about_la_LIBADD = $(LIB_KSYCOCA)
+kio_about_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+noinst_HEADERS = kio_about.h
+
+kdelnk_DATA = about.protocol
+kdelnkdir = $(kde_servicesdir)
+
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kio_about.pot
diff --git a/kioslave/about/about.protocol b/kioslave/about/about.protocol
new file mode 100644
index 000000000..bc685bfe5
--- /dev/null
+++ b/kioslave/about/about.protocol
@@ -0,0 +1,9 @@
+[Protocol]
+exec=kio_about
+protocol=about
+input=none
+output=filesystem
+reading=true
+defaultMimetype=text/html
+Icon=help_index
+Class=:local
diff --git a/kioslave/about/kio_about.cpp b/kioslave/about/kio_about.cpp
new file mode 100644
index 000000000..226de2f4f
--- /dev/null
+++ b/kioslave/about/kio_about.cpp
@@ -0,0 +1,76 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kio_about.h"
+
+#include <stdlib.h>
+#include <qstring.h>
+#include <kinstance.h>
+#include <kurl.h>
+
+using namespace KIO;
+
+AboutProtocol::AboutProtocol(const QCString &pool_socket, const QCString &app_socket)
+ : SlaveBase("about", pool_socket, app_socket)
+{
+}
+
+AboutProtocol::~AboutProtocol()
+{
+}
+
+void AboutProtocol::get( const KURL& )
+{
+ QByteArray output;
+
+ QTextStream os( output, IO_WriteOnly );
+ os.setEncoding( QTextStream::Latin1 ); // In fast ASCII
+
+ os << "<html><head><title>about:blank</title></head><body></body></html>";
+
+ data( output );
+ finished();
+}
+
+void AboutProtocol::mimetype( const KURL& )
+{
+ mimeType("text/html");
+ finished();
+}
+
+extern "C"
+{
+ int KDE_EXPORT kdemain( int argc, char **argv ) {
+
+ KInstance instance("kio_about");
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_about protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ AboutProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
+
diff --git a/kioslave/about/kio_about.h b/kioslave/about/kio_about.h
new file mode 100644
index 000000000..29982dbe7
--- /dev/null
+++ b/kioslave/about/kio_about.h
@@ -0,0 +1,39 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kio_about_h__
+#define __kio_about_h__
+
+#include <qcstring.h>
+
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+
+class AboutProtocol : public KIO::SlaveBase
+{
+public:
+ AboutProtocol(const QCString &pool_socket, const QCString &app_socket);
+ virtual ~AboutProtocol();
+
+ virtual void get(const KURL& url);
+ virtual void mimetype(const KURL& url);
+};
+
+#endif
diff --git a/kioslave/cgi/Makefile.am b/kioslave/cgi/Makefile.am
new file mode 100644
index 000000000..cc71753e9
--- /dev/null
+++ b/kioslave/cgi/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = kcmcgi
+
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = kio_cgi.la
+
+kio_cgi_la_SOURCES = cgi.cpp
+kio_cgi_la_LIBADD = $(LIB_KIO)
+kio_cgi_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = cgi.h
+
+METASOURCES = AUTO
+
+kdelnkdir = $(kde_servicesdir)
+kdelnk_DATA = cgi.protocol
diff --git a/kioslave/cgi/README b/kioslave/cgi/README
new file mode 100644
index 000000000..d68fc9d2c
--- /dev/null
+++ b/kioslave/cgi/README
@@ -0,0 +1,15 @@
+The CGI IO slave provides a way to execute CGI programs without the need to
+have a running web server. This can for example be used for local testing of
+CGI programs or for using search engines that only provide a CGI frontend like
+the one from Doxygen.
+
+The IO slave implements the cgi: protocol. It uses the filename from the given
+URL and searches a configurable list of directories. If it finds an executable
+with the given name it executes it, passes the arguments of the URL and sets the
+environment variables needed by CGI programs.
+
+The kcontrol module System/kcmcgi is used to configure the search paths for CGI
+programs.
+
+If you have questions or comments please contact Cornelius Schumacher
+<schumacher@kde.org>.
diff --git a/kioslave/cgi/cgi.cpp b/kioslave/cgi/cgi.cpp
new file mode 100644
index 000000000..011760e0b
--- /dev/null
+++ b/kioslave/cgi/cgi.cpp
@@ -0,0 +1,273 @@
+/*
+ Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org>
+ Copyright 2006 Michael Pyne <michael.pyne@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <qdir.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+#include "cgi.h"
+
+using namespace KIO;
+
+CgiProtocol::CgiProtocol( const QCString &pool, const QCString &app )
+ : SlaveBase( "cgi", pool, app )
+{
+ kdDebug(7124) << "CgiProtocol::CgiProtocol" << endl;
+
+ KConfig cfg( "kcmcgirc" );
+ cfg.setGroup( "General" );
+ mCgiPaths = cfg.readListEntry( "Paths" );
+}
+
+CgiProtocol::~CgiProtocol()
+{
+ kdDebug(7124) << "CgiProtocol::~CgiProtocol" << endl;
+}
+
+/**
+ * Search in reverse order through a QByteArray for a given character. The position
+ * of the character is returned, or -1 if it was not found.
+ */
+static int qByteArrayFindRev( const QByteArray &ba, char c, int startIndex )
+{
+ for ( int i = startIndex; i >= 0; --i )
+ if ( ba[i] == c ) return i;
+
+ return -1;
+}
+
+/**
+ * Extract data in ba from start, including no more than len characters from ba.
+ * Should be exactly comparable to QCString::mid()
+ */
+static QCString extractQCString( const QByteArray &ba, uint start, uint len = 0xffffffff )
+{
+ uint realLen = len;
+
+ if ( ( ba.size() - start ) < len )
+ realLen = ba.size() - start;
+
+ return QCString( &ba[ start ], realLen + 1 );
+}
+
+/**
+ * Search through a QByteArray for a given string. The position of the string
+ * is returned, or -1 if it was not found.
+ */
+static int qByteArrayFindStr( const QByteArray &ba, const char *str )
+{
+ int strLen = qstrlen( str );
+ int searchLen = ba.size() - strLen;
+
+ for ( int i = 0; i <= searchLen; ++i ) {
+ QCString temp = extractQCString( ba, i, strLen );
+ if ( temp == str )
+ return i;
+ }
+
+ return -1;
+}
+
+void CgiProtocol::get( const KURL& url )
+{
+ kdDebug(7124) << "CgiProtocol::get()" << endl;
+ kdDebug(7124) << " URL: " << url.url() << endl;
+#if 0
+ kdDebug(7124) << " Path: " << url.path() << endl;
+ kdDebug(7124) << " Query: " << url.query() << endl;
+ kdDebug(7124) << " Protocol: " << url.protocol() << endl;
+ kdDebug(7124) << " Filename: " << url.filename() << endl;
+#endif
+ QCString protocol = "SERVER_PROTOCOL=HTTP";
+ putenv( protocol.data() );
+
+ QCString requestMethod = "REQUEST_METHOD=GET";
+ putenv( requestMethod.data() );
+
+ QCString query = url.query().mid( 1 ).local8Bit();
+ query.prepend( "QUERY_STRING=" );
+ putenv( query.data() );
+
+ QString path = url.path();
+
+ QString file;
+
+ int pos = path.findRev('/');
+ if ( pos >= 0 ) file = path.mid( pos + 1 );
+ else file = path;
+
+ QString cmd;
+
+ bool stripHeader = false;
+ bool forwardFile = true;
+
+ QStringList::ConstIterator it;
+ for( it = mCgiPaths.begin(); it != mCgiPaths.end(); ++it ) {
+ cmd = *it;
+ if ( !(*it).endsWith("/") )
+ cmd += "/";
+ cmd += file;
+ if ( KStandardDirs::exists( cmd ) ) {
+ forwardFile = false;
+ stripHeader = true;
+ break;
+ }
+ }
+
+ FILE *fd;
+
+ if ( forwardFile ) {
+ kdDebug(7124) << "Forwarding to '" << path << "'" << endl;
+
+ QCString filepath = QFile::encodeName( path );
+
+ fd = fopen( filepath.data(), "r" );
+
+ if ( !fd ) {
+ kdDebug(7124) << "Error opening '" << filepath << "'" << endl;
+ error( KIO::ERR_CANNOT_OPEN_FOR_READING, filepath );
+ return;
+ }
+ } else {
+ kdDebug(7124) << "Cmd: " << cmd << endl;
+
+ fd = popen( QFile::encodeName(KProcess::quote( cmd )).data(), "r" );
+
+ if ( !fd ) {
+ kdDebug(7124) << "Error running '" << cmd << "'" << endl;
+ error( KIO::ERR_CANNOT_OPEN_FOR_READING, cmd );
+ return;
+ }
+ }
+
+ char buffer[ 4090 ];
+
+ while ( !feof( fd ) )
+ {
+ int n = fread( buffer, 1, 2048, fd );
+
+ if ( n == -1 )
+ {
+ // ERROR
+ if ( forwardFile ) {
+ fclose( fd );
+ } else {
+ pclose( fd );
+ }
+ return;
+ }
+
+ buffer[n] = 0;
+
+ if ( stripHeader ) {
+ QByteArray output;
+
+ // Access the buffer in-place by using setRawData()
+ output.setRawData( buffer, n );
+
+ int colon = output.find( ':' );
+ int newline = output.find( '\n' );
+ int semicolon = qByteArrayFindRev( output, ';', newline );
+ int end;
+ if ( semicolon < 0 ) end = newline;
+ else end = semicolon;
+
+#if 0
+ kdDebug(7124) << " colon: " << colon << endl;
+ kdDebug(7124) << " newline: " << newline << endl;
+ kdDebug(7124) << " semicolon: " << semicolon << endl;
+ kdDebug(7124) << " end: " << end << endl;
+#endif
+
+ QCString contentType = extractQCString( output, colon + 1, end - colon - 1 );
+
+ contentType = contentType.stripWhiteSpace();
+
+ kdDebug(7124) << "ContentType: '" << contentType << "'" << endl;
+
+ mimeType( contentType );
+
+ int start = qByteArrayFindStr( output, "\r\n\r\n" );
+ if ( start >= 0 ) start += 4;
+ else {
+ start = qByteArrayFindStr( output, "\n\n" );
+ if ( start >= 0 ) start += 2;
+ }
+
+ if ( start < 0 )
+ start = 0;
+
+ // We're done with the part of the buffer we're using.
+ output.resetRawData ( buffer, n );
+
+ // Now access the part of the buffer after the header.
+ output.setRawData ( buffer + start, n - start );
+ data( output );
+ output.resetRawData ( buffer + start, n - start );
+
+ stripHeader = false;
+ } else {
+ QByteArray array;
+ array.setRawData( buffer, n );
+ data( array );
+ array.resetRawData( buffer, n );
+ }
+ }
+
+ if ( forwardFile ) {
+ fclose( fd );
+ } else {
+ pclose( fd );
+ }
+
+ finished();
+
+ kdDebug(7124) << "CgiProtocol::get - done" << endl;
+}
+
+extern "C" { int KDE_EXPORT kdemain( int argc, char **argv ); }
+
+/*! The kdemain function generates an instance of the ioslave and starts its
+ * dispatch loop. */
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_cgi" );
+
+ kdDebug(7124) << "kio_cgi starting " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_cgi protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ CgiProtocol slave( argv[2], argv[3] );
+ slave.dispatchLoop();
+
+ return 0;
+}
diff --git a/kioslave/cgi/cgi.h b/kioslave/cgi/cgi.h
new file mode 100644
index 000000000..fec90aa59
--- /dev/null
+++ b/kioslave/cgi/cgi.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#ifndef KIO_CGI_H
+#define KIO_CGI_H
+
+#include <qobject.h>
+
+#include <kio/slavebase.h>
+
+class KProcess;
+
+/*!
+ This class implements an ioslave for viewing CGI script output without the
+ need to run a web server.
+*/
+class CgiProtocol : public KIO::SlaveBase
+{
+ public:
+ CgiProtocol( const QCString &pool, const QCString &app );
+ virtual ~CgiProtocol();
+
+ virtual void get( const KURL& url );
+
+// virtual void mimetype( const KURL& url );
+
+ protected:
+// QCString errorMessage();
+
+ private:
+ QStringList mCgiPaths;
+};
+
+#endif
diff --git a/kioslave/cgi/cgi.protocol b/kioslave/cgi/cgi.protocol
new file mode 100644
index 000000000..9c6cc378e
--- /dev/null
+++ b/kioslave/cgi/cgi.protocol
@@ -0,0 +1,8 @@
+[Protocol]
+exec=kio_cgi
+protocol=cgi
+input=none
+output=filesystem
+reading=true
+DocPath=kioslave/cgi.html
+Icon=html
diff --git a/kioslave/cgi/kcmcgi/Makefile.am b/kioslave/cgi/kcmcgi/Makefile.am
new file mode 100644
index 000000000..abfef594b
--- /dev/null
+++ b/kioslave/cgi/kcmcgi/Makefile.am
@@ -0,0 +1,17 @@
+
+kde_module_LTLIBRARIES = kcm_cgi.la
+
+kcm_cgi_la_SOURCES = kcmcgi.cpp
+kcm_cgi_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+kcm_cgi_la_LIBADD = -lkdeui $(LIB_KIO)
+
+INCLUDES= $(all_includes)
+
+kcm_cgi_la_METASOURCES = AUTO
+
+noinst_HEADERS = kcmcgi.h
+
+xdg_apps_DATA = kcmcgi.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kcmcgi.pot
diff --git a/kioslave/cgi/kcmcgi/kcmcgi.cpp b/kioslave/cgi/kcmcgi/kcmcgi.cpp
new file mode 100644
index 000000000..18436e8d9
--- /dev/null
+++ b/kioslave/cgi/kcmcgi/kcmcgi.cpp
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kaboutdata.h>
+#include <kfiledialog.h>
+
+#include <qlayout.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+
+#include "kcmcgi.h"
+#include "kcmcgi.moc"
+
+extern "C"
+{
+ KDE_EXPORT KCModule *create_cgi( QWidget *parent, const char * )
+ {
+ KGlobal::locale()->insertCatalogue("kcmcgi");
+ return new KCMCgi( parent, "kcmcgi" );
+ }
+}
+
+
+KCMCgi::KCMCgi(QWidget *parent, const char *name)
+ : KCModule(parent, name)
+{
+ setButtons(Default|Apply);
+
+ QVBoxLayout *topLayout = new QVBoxLayout(this, 0, KDialog::spacingHint());
+
+ QGroupBox *topBox = new QGroupBox( 1, Horizontal, i18n("Paths to Local CGI Programs"), this );
+ topLayout->addWidget( topBox );
+
+ mListBox = new QListBox( topBox );
+
+ QHBox *buttonBox = new QHBox( topBox );
+ buttonBox->setSpacing( KDialog::spacingHint() );
+
+ mAddButton = new QPushButton( i18n("Add..."), buttonBox );
+ connect( mAddButton, SIGNAL( clicked() ), SLOT( addPath() ) );
+
+ mRemoveButton = new QPushButton( i18n("Remove"), buttonBox );
+ connect( mRemoveButton, SIGNAL( clicked() ), SLOT( removePath() ) );
+ connect( mListBox, SIGNAL( clicked ( QListBoxItem * )),this, SLOT( slotItemSelected( QListBoxItem *)));
+
+ mConfig = new KConfig("kcmcgirc");
+
+ load();
+ updateButton();
+ KAboutData *about =
+ new KAboutData( I18N_NOOP("kcmcgi"),
+ I18N_NOOP("CGI KIO Slave Control Module"),
+ 0, 0, KAboutData::License_GPL,
+ I18N_NOOP("(c) 2002 Cornelius Schumacher") );
+
+ about->addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
+ setAboutData(about);
+}
+
+KCMCgi::~KCMCgi()
+{
+ delete mConfig;
+}
+
+void KCMCgi::slotItemSelected( QListBoxItem * )
+{
+ updateButton();
+}
+
+void KCMCgi::updateButton()
+{
+ mRemoveButton->setEnabled( mListBox->selectedItem ());
+}
+
+void KCMCgi::defaults()
+{
+ mListBox->clear();
+ updateButton();
+}
+
+void KCMCgi::save()
+{
+ QStringList paths;
+
+ uint i;
+ for( i = 0; i < mListBox->count(); ++i ) {
+ paths.append( mListBox->text( i ) );
+ }
+
+ mConfig->setGroup( "General" );
+ mConfig->writeEntry( "Paths", paths );
+
+ mConfig->sync();
+}
+
+void KCMCgi::load()
+{
+ mConfig->setGroup( "General" );
+ QStringList paths = mConfig->readListEntry( "Paths" );
+
+ mListBox->insertStringList( paths );
+}
+
+void KCMCgi::addPath()
+{
+ QString path = KFileDialog::getExistingDirectory( QString::null, this );
+
+ if ( !path.isEmpty() ) {
+ mListBox->insertItem( path );
+ emit changed( true );
+ }
+ updateButton();
+}
+
+void KCMCgi::removePath()
+{
+ int index = mListBox->currentItem();
+ if ( index >= 0 ) {
+ mListBox->removeItem( index );
+ emit changed( true );
+ }
+ updateButton();
+}
+
+QString KCMCgi::quickHelp() const
+{
+ return i18n("<h1>CGI Scripts</h1> The CGI KIO slave lets you execute "
+ "local CGI programs without the need to run a web server. "
+ "In this control module you can configure the paths that "
+ "are searched for CGI scripts.");
+}
diff --git a/kioslave/cgi/kcmcgi/kcmcgi.desktop b/kioslave/cgi/kcmcgi/kcmcgi.desktop
new file mode 100644
index 000000000..a948a38b4
--- /dev/null
+++ b/kioslave/cgi/kcmcgi/kcmcgi.desktop
@@ -0,0 +1,231 @@
+[Desktop Entry]
+Icon=run
+Type=Application
+Exec=kcmshell kcmcgi
+DocPath=
+X-KDE-ModuleType=Library
+X-KDE-Library=cgi
+
+Name=CGI Scripts
+Name[af]=CGI Skripte
+Name[ar]=نصوص CGI البرمجية
+Name[az]=CGI Skriptləri
+Name[be]=Сцэнары CGI
+Name[bg]=CGI скриптове
+Name[bn]=সি-জি-আই স্ক্রিপ্ট
+Name[br]=Urzhiaouegoù CGI
+Name[bs]=CGI skripte
+Name[ca]=Scripts CGI
+Name[cs]=CGI skripty
+Name[csb]=Skriptë CGI
+Name[cy]=Sgriptiau CGI
+Name[da]=CGI Scripter
+Name[de]=CGI-Skripte
+Name[el]=Σενάρια CGI
+Name[eo]=CGI-Skriptaĵoj
+Name[es]=Procedimientos CGI
+Name[et]=CGI skriptid
+Name[eu]=CGI scriptak
+Name[fa]=دست‌نوشته‌های CGI
+Name[fi]=CGI-komentosarjat
+Name[fr]=Scripts CGI
+Name[fy]=CGI-skripts
+Name[ga]=Scripteanna CGI
+Name[gl]=Guións CGI
+Name[he]=תסריטי CGI
+Name[hi]=सीजीआई स्क्रिप्ट
+Name[hr]=CGI skripte
+Name[hu]=CGI-programok
+Name[is]=CGI Skriftur
+Name[it]=Script CGI
+Name[ja]=CGI スクリプト
+Name[ka]=CGI სკრიპტები
+Name[kk]=CGI скрипттері
+Name[km]=ស្គ្រីប CGI
+Name[ko]=CGI 스크립트
+Name[lo]=ໂໍຊລາລີສ
+Name[lt]=CGI scenarijai
+Name[lv]=CGI Skripts
+Name[mk]=CGI-скрипти
+Name[mn]=CGI-Скрипт
+Name[ms]=Skrip CGI
+Name[mt]=Scripts CGI
+Name[nb]=CGI-skript
+Name[nds]=CGI-Skripten
+Name[ne]=CGI स्क्रिप्ट
+Name[nl]=CGI-scripts
+Name[nn]=CGI-skript
+Name[nso]=Ditshwaelo tsa CGI
+Name[pa]=CGI ਸਕ੍ਰਿਪਟਾਂ
+Name[pl]=Skrypty CGI
+Name[pt]=Programas CGI
+Name[pt_BR]=Scripts CGI
+Name[ro]=Scripturi CGI
+Name[ru]=Сценарии CGI
+Name[rw]=Agaporogaramu CGI
+Name[se]=CGI-skriptat
+Name[sk]=Skripty CGI
+Name[sl]=Skripte CGI
+Name[sr]=CGI Скрипте
+Name[sr@Latn]=CGI Skripte
+Name[sv]=CGI-skript
+Name[ta]=CGI எழுத்தாக்கங்கள்
+Name[te]=సిజిఐ స్క్రిప్ట్లు
+Name[tg]=Дастнависи CGI
+Name[th]=สคริปต์ CGI
+Name[tr]=CD Betikleri
+Name[tt]=CGI Ämerleklär
+Name[uk]=Скрипти CGI
+Name[uz]=CGI skriptlar
+Name[uz@cyrillic]=CGI скриптлар
+Name[ven]=Zwikiriputi zwa CGI
+Name[vi]=Văn lệnh CGI
+Name[wa]=Scripe CGI
+Name[xh]=Amagama ashicilelwe phantsi CGI
+Name[zh_CN]=CGI 脚本
+Name[zh_TW]=CGI 命令稿
+Name[zu]=Izikript ze-CGI
+Comment=Configure the CGI KIO slave
+Comment[af]=Stel die CGI KIO slaaf op
+Comment[ar]=تهيئة CGI KIO slave
+Comment[be]=Настаўленні CGI KIO slave
+Comment[bg]=Настройване на модула за изпълнение на скриптове без уеб сървър - CGI KIO
+Comment[bn]=CGI KIO স্লেভ কনফিগার করুন
+Comment[bs]=Podešavanje CGI KIO slave-a
+Comment[ca]=Configura l'esclau KIO CGI
+Comment[cs]=Nastavení CGI pro KDE
+Comment[csb]=Kònfigùracëjô procedurë òbsłużënkù CGI
+Comment[cy]=Ffurfweddu'r gwas CGI KIO
+Comment[da]=Indstilling af CGI KIO-slaven
+Comment[de]=Ein-/Ausgabemodul für CGI einrichten
+Comment[el]=Ρυθμίστε το CGI KIO slave
+Comment[eo]=Agordu CGI-enel-sklavon
+Comment[es]=Configuración del KIO slave de CGI
+Comment[et]=CGI KIO mooduli seadistamine
+Comment[eu]=CGI KIO slave-a konfiguratu
+Comment[fa]=پیکربندی پی‌رو CGI KIO
+Comment[fi]=Muokkaa CGI-KIO-palvelun asetuksia
+Comment[fr]=Configuration du CGI KIO slave
+Comment[fy]=Hjir kinne jo de CGI Kio-slave ynstelle
+Comment[ga]=Cumraigh an sclábhaí CGI KIO
+Comment[gl]=Configuración do escravo KIO de CGI
+Comment[he]=שינוי הגדרות פרוטוקול ה־CGI
+Comment[hi]=सीजीआई केआईओ स्लेव कॉन्फ़िगर करें
+Comment[hr]=Konfiguriranje CGI KIO podčinjenog
+Comment[hu]=A CGI KDE-protokoll beállításai
+Comment[is]=Stilla CGI þrælinn
+Comment[it]=Configura il KIO-slave CGI
+Comment[ja]=CGI KIO スレーブの設定
+Comment[ka]=CGI KIO slave-ის კონფიგურაცია
+Comment[kk]=CGI KIO slave-ты баптау
+Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​កូនចៅ CGI KIO
+Comment[ko]=CGI KIO 슬레이브 설정
+Comment[lo]=ປັບແຕ່ງກ້ອງ
+Comment[lt]=Konfigūruoti CGI KIO slave
+Comment[lv]=Konfigurēt CGI KIO vergu
+Comment[mk]=Конфигурација на CGI KIO служителот
+Comment[mn]=CGI-Оролт/Гаралтын-Модул тохируулах
+Comment[ms]=Konfigur hamba CGI KIO
+Comment[mt]=Ikkonfigura l-iskjav CGI
+Comment[nb]=Tilpass CGI KIO slave
+Comment[nds]=Den CGI-In-/Utgaavdeenst instellen
+Comment[ne]=CGI KIO स्लेभ कन्फिगर गर्नुहोस्
+Comment[nl]=Hier kunt u de CGI Kio-slave instellen
+Comment[nn]=Set opp CGI-KIO-slaven
+Comment[nso]=Beakanya lekgoba la KIO ya CGI
+Comment[pa]=CGI KIO ਸਲੇਵ ਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja procedury obsługi CGI
+Comment[pt]=Configuração do KIO slave de CGIs
+Comment[pt_BR]=Configurar o KIO (escravo) do CGI
+Comment[ro]=Configurează dispozitivul I/O CGI
+Comment[ru]=Настройка CGI KIO slave
+Comment[rw]=Kuboneza CGI KIO umugaragu
+Comment[se]=Heivet CGI-KIO-šláva
+Comment[sk]=Nastavenie IO klienta CGI
+Comment[sl]=Nastavi podrejenega KIO CGI
+Comment[sr]=Подешавање CGI KIO slave-а
+Comment[sr@Latn]=Podešavanje CGI KIO slave-a
+Comment[sv]=Anpassa I/O-slaven för CGI
+Comment[ta]=CGI KIO slaveஐ வடிவமை
+Comment[tg]=Бандаи CGI KIO-ро танзим кунед
+Comment[th]=ปรับแต่ง CGI KIO slave
+Comment[tr]=CGI KIO aracısını yapılandır
+Comment[tt]=CGI KIO slave caylawı
+Comment[uk]=Налаштування підлеглого KIO CGI
+Comment[uz]=CGI KIO sleyvni moslash
+Comment[uz@cyrillic]=CGI KIO слейвни мослаш
+Comment[ven]=Dzudzanyani phuli CGI KIO
+Comment[vi]=Cấu hình đày tớCGI KIO
+Comment[wa]=Apontyî li mandaye KIO CGI
+Comment[xh]=Qwlalsela i CGI KIO slave
+Comment[zh_CN]=配置 CGI KIO 仆人
+Comment[zh_TW]=設定 CGI KIO slave
+Comment[zu]=Hlanganisela i-CGI KIO slave
+
+Keywords=CGI,KIO,Slave,Paths
+Keywords[ar]=CGI,KIO,Slave,Paths,مسارات
+Keywords[az]=CGI,KIO,Slave,Paths,Cığırlar
+Keywords[be]=Шляхі,CGI,KIO,Slave,Paths
+Keywords[bg]=скриптове, уеб, динамичен, скрипт, Интернет, път, пътища, CGI, KIO, Slave, Paths
+Keywords[br]=CGI,KIO,sklav,hentoù
+Keywords[ca]=CGI,KIO,Esclau,Rutes
+Keywords[cs]=CGI,KIO,slave,cesty
+Keywords[csb]=CGI,KIO,procedurë wé/wi,stegnë
+Keywords[cy]=CGI,KIO,Gwas,Llwybrau
+Keywords[da]=CGI,KIO,Slave,Stier
+Keywords[de]=CGI,KIO,Ein-/Ausgabemodul,Pfade
+Keywords[el]=CGI,KIO,Slave,Διαδρομές
+Keywords[eo]=CGI,Enel,K-enel,sklavo,servo,vojoj
+Keywords[es]=CGI,KIO,Slave,Rutas
+Keywords[et]=CGI,KIO,moodul,otsinguteed
+Keywords[fa]=CGI، KIO، Slave، مسیرها
+Keywords[fi]=CGI,KIO,KIO-palvelu,palvelu,Polut
+Keywords[fr]=CGI,KIO,Slave,Paths,Chemins,Emplacements
+Keywords[fy]=cgi,kio,slave,paths,paden
+Keywords[ga]=CGI,KIO,Sclábhaí,Bealaí
+Keywords[gl]=CGI,KIO,Escravo,Camiños
+Keywords[he]=CGI,KIO,פרוטוקול,נתיבים, Slave,Paths
+Keywords[hi]=सीजीआई,केआईओ,स्लेव,पथ
+Keywords[hr]=CGI,KIO,Slave,Paths,podčinjeni,putanje
+Keywords[hu]=CGI,KIO,protokoll,elérési utak
+Keywords[is]=CGI,KIO,þræll,slóðir
+Keywords[it]=CGI,KIO,kioslave,percorsi
+Keywords[ja]=CGI,KIO,スレーブ,パス
+Keywords[km]=CGI,KIO,កូនចៅ,ផ្លូវ
+Keywords[lt]=CGI,KIO,Slave,Paths, keliai
+Keywords[lv]=CGI,KIO,vergi,ceļi
+Keywords[mk]=CGI,KIO,Slave,Paths,Патеки
+Keywords[mn]=CGI,KIO,Оролт/Гаралтын-Модул,Зам
+Keywords[mt]=CGI,KIO,Slave,Paths,skjav,passaġġ
+Keywords[nb]=CGI,KIO,Slave,slave,stier
+Keywords[nds]=CGI,KIO,Slave,IU,In-/Utgaavdeenst,Deenst,Padden
+Keywords[ne]=CGI,KIO,स्लेभ, मार्ग
+Keywords[nl]=cgi,kio,slave,paths,paden
+Keywords[nn]=CGI,KIO,slave,stiar
+Keywords[nso]=CGI,KIO,Lekgoba,Ditsejana
+Keywords[pa]=CGI,KIO,ਸਲੇਵ,ਮਾਰਗ
+Keywords[pl]=CGI,KIO,procedury we/wy,ścieżki
+Keywords[pt]=CGI,KIO,Slave,Localizações
+Keywords[pt_BR]=CGI,KIO,Escravo,Caminhos
+Keywords[ro]=I/E,IE,CGI,KIO,dispozitiv,căi
+Keywords[rw]=CGI,KIO,Umugaragu,Inzira
+Keywords[se]=CGI,KIO,šláva,bálgát
+Keywords[sk]=CGI,KIO,klient,cesty
+Keywords[sl]=CGI,KIO,podrejeni,pot
+Keywords[sr]=CGI,KIO,Slave,Путање
+Keywords[sr@Latn]=CGI,KIO,Slave,Putanje
+Keywords[sv]=CGI,KIO,Slav,Sökvägar
+Keywords[ta]=CGI,KIO,ஸ்லேவ்,பாதைகள்
+Keywords[te]=సిజిఐ,కెఐఒ,బానిస,దారులు
+Keywords[th]=CGI,KIO,Slave,เส้นทาง
+Keywords[tr]=CGI,KIO,Aracı,Yollar
+Keywords[uk]=CGI,KIO,підлеглий,шлях
+Keywords[uz]=CGI,KIO,Sleyv,Yoʻllar
+Keywords[uz@cyrillic]=CGI,KIO,Слейв,Йўллар
+Keywords[ven]=CGI,KIO,Phuli,Ludila
+Keywords[vi]=CGI,KIO,Đày tớ,Đường dẫn
+Keywords[wa]=CGI,KIO,Slave,Paths,tchimins,mandaye
+Keywords[zh_CN]=CGI,KIO,Slave,Paths,路径
+Keywords[zh_TW]=CGI,KIO,Slave,Paths,路徑
+Keywords[zu]=CGI,KIO,Slave,Izindlela
+Categories=Qt;KDE;X-KDE-settings-webbrowsing;
diff --git a/kioslave/cgi/kcmcgi/kcmcgi.h b/kioslave/cgi/kcmcgi/kcmcgi.h
new file mode 100644
index 000000000..10e4e3385
--- /dev/null
+++ b/kioslave/cgi/kcmcgi/kcmcgi.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#ifndef KCMCGI_H
+#define KCMCGI_H
+
+#include <kcmodule.h>
+
+class QListBox;
+class QPushButton;
+
+class KConfig;
+
+class KCMCgi : public KCModule
+{
+ Q_OBJECT
+ public:
+ KCMCgi( QWidget *parent = 0, const char *name = 0 );
+ ~KCMCgi();
+
+ void load();
+ void save();
+ void defaults();
+ QString quickHelp() const;
+
+ public slots:
+
+ protected slots:
+ void addPath();
+ void removePath();
+ void slotItemSelected( QListBoxItem * item );
+ private:
+ void updateButton();
+ QListBox *mListBox;
+ QPushButton *mAddButton;
+ QPushButton *mRemoveButton;
+
+ KConfig *mConfig;
+};
+
+#endif
diff --git a/kioslave/configure.in.bot b/kioslave/configure.in.bot
new file mode 100644
index 000000000..11b66748d
--- /dev/null
+++ b/kioslave/configure.in.bot
@@ -0,0 +1,7 @@
+if test -z "$SASL2_LIBS"; then
+ echo ""
+ echo "cyrus-sasl 2 library is missing. The pop3 and smtp ioslaves will lack of a lot of authentication methods."
+ echo "See http://asg.web.cmu.edu/sasl/sasl-library.html or your distribution's packages."
+ echo ""
+ all_tests=bad
+fi
diff --git a/kioslave/configure.in.in b/kioslave/configure.in.in
new file mode 100644
index 000000000..331a300b8
--- /dev/null
+++ b/kioslave/configure.in.in
@@ -0,0 +1,15 @@
+KDE_CHECK_SSL
+
+sasl2_header="no"
+SASL2_LIBS=""
+
+KDE_CHECK_HEADERS(sasl.h)dnl SASL1 header is enough for kio_ldap
+KDE_CHECK_HEADERS(sasl/sasl.h, sasl2_header="yes")
+if test "$sasl2_header" = "yes" ; then
+ KDE_CHECK_LIB(sasl2, sasl_client_init, SASL2_LIBS="-lsasl2")
+fi
+
+if test "x$SASL2_LIBS" != "x" ; then
+ AC_DEFINE_UNQUOTED(HAVE_LIBSASL2, 1, [Define if you have cyrus-sasl2 libraries])
+fi
+AC_SUBST(SASL2_LIBS)
diff --git a/kioslave/filter/Makefile.am b/kioslave/filter/Makefile.am
new file mode 100644
index 000000000..f315064c8
--- /dev/null
+++ b/kioslave/filter/Makefile.am
@@ -0,0 +1,21 @@
+KDE_CPPFLAGS = -DQT_NO_CAST_ASCII
+
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = kio_filter.la
+
+kio_filter_la_SOURCES = filter.cc
+kio_filter_la_LIBADD = $(LIB_KSYCOCA)
+kio_filter_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+noinst_HEADERS = filter.h
+
+METASOURCES = AUTO
+
+protocoldir = $(kde_servicesdir)
+
+if include_BZIP2
+BZIP2FILES=bzip.protocol bzip2.protocol
+endif
+
+protocol_DATA = gzip.protocol $(BZIP2FILES)
+
diff --git a/kioslave/filter/bzip.protocol b/kioslave/filter/bzip.protocol
new file mode 100644
index 000000000..65564992a
--- /dev/null
+++ b/kioslave/filter/bzip.protocol
@@ -0,0 +1,10 @@
+[Protocol]
+exec=kio_filter
+protocol=bzip
+mimetype=application/x-bzip
+input=stream
+output=stream
+reading=true
+source=false
+DocPath=kioslave/bzip.html
+Icon=ark
diff --git a/kioslave/filter/bzip2.protocol b/kioslave/filter/bzip2.protocol
new file mode 100644
index 000000000..c7d7c782a
--- /dev/null
+++ b/kioslave/filter/bzip2.protocol
@@ -0,0 +1,10 @@
+[Protocol]
+exec=kio_filter
+protocol=bzip2
+mimetype=application/x-bzip2
+input=stream
+output=stream
+reading=true
+source=false
+DocPath=kioslave/bzip2.html
+Icon=ark
diff --git a/kioslave/filter/configure.in.in b/kioslave/filter/configure.in.in
new file mode 100644
index 000000000..5ef522b6f
--- /dev/null
+++ b/kioslave/filter/configure.in.in
@@ -0,0 +1 @@
+AC_FIND_BZIP2
diff --git a/kioslave/filter/filter.cc b/kioslave/filter/filter.cc
new file mode 100644
index 000000000..fe7cbc154
--- /dev/null
+++ b/kioslave/filter/filter.cc
@@ -0,0 +1,167 @@
+/*
+This file is part of KDE
+
+ Copyright (C) 1999-2000 Waldo Bastian (bastian@kde.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <kinstance.h>
+#include <kdebug.h>
+#include <kmimemagic.h>
+#include <kfilterbase.h>
+
+#include "filter.h"
+
+extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); }
+
+int kdemain( int argc, char ** argv)
+{
+ KInstance instance( "kio_filter" );
+
+ kdDebug(7110) << "Starting " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_filter protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ FilterProtocol slave(argv[1], argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ kdDebug(7110) << "Done" << endl;
+ return 0;
+}
+
+FilterProtocol::FilterProtocol( const QCString & protocol, const QCString &pool, const QCString &app )
+ : KIO::SlaveBase( protocol, pool, app )
+{
+ QString mimetype = QString::fromLatin1("application/x-") + QString::fromLatin1(protocol);
+ filter = KFilterBase::findFilterByMimeType( mimetype );
+ Q_ASSERT(filter);
+}
+
+void FilterProtocol::get( const KURL & )
+{
+ if (subURL.isEmpty())
+ {
+ error( KIO::ERR_NO_SOURCE_PROTOCOL, mProtocol );
+ return;
+ }
+ if (!filter)
+ {
+ error( KIO::ERR_INTERNAL, mProtocol );
+ return;
+ }
+ needSubURLData();
+
+ filter->init(IO_ReadOnly);
+
+ bool bNeedHeader = true;
+ bool bNeedMimetype = true;
+ bool bError = true;
+ int result;
+
+ QByteArray inputBuffer;
+ QByteArray outputBuffer(8*1024); // Start with a modest buffer
+ filter->setOutBuffer( outputBuffer.data(), outputBuffer.size() );
+ while(true)
+ {
+ if (filter->inBufferEmpty())
+ {
+ dataReq(); // Request data
+ result = readData( inputBuffer);
+ kdDebug(7110) << "requestData: got " << result << endl;
+ if (result <= 0)
+ {
+ bError = true;
+ break; // Unexpected EOF.
+ }
+ filter->setInBuffer( inputBuffer.data(), inputBuffer.size() );
+ }
+ if (bNeedHeader)
+ {
+ bError = !filter->readHeader();
+ if (bError)
+ break;
+ bNeedHeader = false;
+ }
+ result = filter->uncompress();
+ if ((filter->outBufferAvailable() == 0) || (result == KFilterBase::END))
+ {
+ kdDebug(7110) << "avail_out = " << filter->outBufferAvailable() << endl;
+ if (filter->outBufferAvailable() != 0)
+ {
+ // Discard unused space :-)
+ outputBuffer.resize(outputBuffer.size() - filter->outBufferAvailable());
+ }
+ if (bNeedMimetype)
+ {
+ KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( outputBuffer, subURL.fileName() );
+ kdDebug(7110) << "Emitting mimetype " << result->mimeType() << endl;
+ mimeType( result->mimeType() );
+ bNeedMimetype = false;
+ }
+ data( outputBuffer ); // Send data
+ filter->setOutBuffer( outputBuffer.data(), outputBuffer.size() );
+ if (result == KFilterBase::END)
+ break; // Finished.
+ }
+ if (result != KFilterBase::OK)
+ {
+ bError = true;
+ break; // Error
+ }
+ }
+
+ if (!bError)
+ {
+ dataReq(); // Request data
+ result = readData( inputBuffer);
+ kdDebug(7110) << "requestData: got " << result << "(expecting 0)" << endl;
+ data( QByteArray() ); // Send EOF
+ }
+
+ filter->terminate();
+
+ if (bError)
+ {
+ error(KIO::ERR_COULD_NOT_READ, subURL.url());
+ subURL = KURL(); // Clear subURL
+ return;
+ }
+
+ subURL = KURL(); // Clear subURL
+ finished();
+}
+
+void FilterProtocol::put( const KURL &/*url*/, int, bool /*_overwrite*/, bool /*_resume*/ )
+{
+ error( KIO::ERR_UNSUPPORTED_ACTION, QString::fromLatin1("put"));
+}
+
+void FilterProtocol::setSubURL(const KURL &url)
+{
+ subURL = url;
+}
+
diff --git a/kioslave/filter/filter.h b/kioslave/filter/filter.h
new file mode 100644
index 000000000..51b56061b
--- /dev/null
+++ b/kioslave/filter/filter.h
@@ -0,0 +1,46 @@
+/*
+This file is part of KDE
+
+ Copyright (C) 2000 Waldo Bastian (bastian@kde.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __filter_h__
+#define __filter_h__
+
+#include <qobject.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+class FilterProtocol : public QObject, public KIO::SlaveBase
+{
+public:
+ FilterProtocol( const QCString & protocol, const QCString &pool, const QCString &app );
+
+ virtual void get( const KURL &url );
+ virtual void put( const KURL &url, int _mode, bool _overwrite,
+ bool _resume );
+ virtual void setSubURL(const KURL &url);
+
+private:
+ KURL subURL;
+ KFilterBase * filter;
+};
+
+#endif
diff --git a/kioslave/filter/gzip.protocol b/kioslave/filter/gzip.protocol
new file mode 100644
index 000000000..75908e381
--- /dev/null
+++ b/kioslave/filter/gzip.protocol
@@ -0,0 +1,10 @@
+[Protocol]
+exec=kio_filter
+protocol=gzip
+mimetype=application/gzip
+input=stream
+output=stream
+reading=true
+source=false
+DocPath=kioslave/gzip.html
+Icon=ark
diff --git a/kioslave/finger/Makefile.am b/kioslave/finger/Makefile.am
new file mode 100644
index 000000000..6ddf78726
--- /dev/null
+++ b/kioslave/finger/Makefile.am
@@ -0,0 +1,27 @@
+## Makfile.am for kio_finger
+## Edit from Makefile.am of kdebase/kioslave/man
+
+INCLUDES= $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_finger.la
+
+kio_finger_la_SOURCES = kio_finger.cpp
+kio_finger_la_LIBADD = -lkio
+kio_finger_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = kio_finger.h
+
+kdelnk_DATA = finger.protocol
+kdelnkdir = $(kde_servicesdir)
+
+kio_finger_data_DATA = kio_finger.pl kio_finger.css
+kio_finger_datadir = $(kde_datadir)/kio_finger
+EXTRA_DIST=$(kio_finger_data_DATA)
+
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_finger.pot
diff --git a/kioslave/finger/finger.protocol b/kioslave/finger/finger.protocol
new file mode 100644
index 000000000..71d1b8e93
--- /dev/null
+++ b/kioslave/finger/finger.protocol
@@ -0,0 +1,9 @@
+[Protocol]
+exec=kio_finger
+protocol=finger
+input=none
+output=stream
+reading=true
+defaultMimetype=text/html
+DocPath=kioslave/finger.html
+Icon=kdmconfig
diff --git a/kioslave/finger/kio_finger.cpp b/kioslave/finger/kio_finger.cpp
new file mode 100644
index 000000000..c940998b9
--- /dev/null
+++ b/kioslave/finger/kio_finger.cpp
@@ -0,0 +1,266 @@
+
+/***************************************************************************
+ kio_finger.cpp - description
+ -------------------
+ begin : Sun Aug 12 2000
+ copyright : (C) 2000 by Andreas Schlapbach
+ email : schlpbch@iam.unibe.ch
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <qtextstream.h>
+#include <qdict.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include "kio_finger.h"
+
+
+using namespace KIO;
+
+static const QString defaultRefreshRate = "60";
+
+extern "C"
+{
+ KDE_EXPORT int kdemain( int argc, char **argv )
+ {
+ KInstance instance( "kio_finger" );
+
+ //kdDebug() << "*** Starting kio_finger " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_finger protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ FingerProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ //kdDebug() << "*** kio_finger Done" << endl;
+ return 0;
+ }
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+FingerProtocol::FingerProtocol(const QCString &pool_socket, const QCString &app_socket)
+ : QObject(), SlaveBase("finger", pool_socket, app_socket)
+{
+ myStdStream = new QString();
+ getProgramPath();
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+FingerProtocol::~FingerProtocol()
+{
+ //kdDebug() << "FingerProtocol::~FingerProtocol()" << endl;
+ delete myURL;
+ delete myPerlPath;
+ delete myFingerPath;
+ delete myFingerPerlScript;
+ delete myFingerCSSFile;
+ delete myStdStream;
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+void FingerProtocol::get(const KURL& url )
+{
+ //kdDebug() << "kio_finger::get(const KURL& url)" << endl ;
+
+ this->parseCommandLine(url);
+
+ //kdDebug() << "myURL: " << myURL->prettyURL() << endl;
+
+ // Reset the stream
+ *myStdStream="";
+
+ QString query = myURL->query();
+ QString refreshRate = defaultRefreshRate;
+
+ //kdDebug() << "query: " << query << endl;
+
+ // Check the validity of the query
+
+ QRegExp regExp("?refreshRate=[0-9][0-9]*", true, true);
+ if (query.contains(regExp)) {
+ //kdDebug() << "looks like a valid query" << endl;
+ QRegExp regExp( "([0-9]+)" );
+ regExp.search(query);
+ refreshRate = regExp.cap(0);
+ }
+
+ //kdDebug() << "Refresh rate: " << refreshRate << endl;
+
+ myKProcess = new KProcess();
+ *myKProcess << *myPerlPath << *myFingerPerlScript
+ << *myFingerPath << *myFingerCSSFile
+ << refreshRate << myURL->host() << myURL->user() ;
+
+ connect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+ //connect(myKProcess, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ // this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ data(QCString(myStdStream->local8Bit()));
+
+ data(QByteArray());
+ finished();
+
+ //clean up
+
+ delete myKProcess;
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+void FingerProtocol::slotGetStdOutput(KProcess* /* p */, char *s, int len)
+{
+ //kdDebug() << "void FingerProtocol::slotGetStdoutOutput()" << endl;
+ *myStdStream += QString::fromLocal8Bit(s, len);
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+void FingerProtocol::mimetype(const KURL & /*url*/)
+{
+ mimeType("text/html");
+ finished();
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+
+
+void FingerProtocol::getProgramPath()
+{
+ //kdDebug() << "kfingerMainWindow::getProgramPath()" << endl;
+ // Not to sure wether I'm using the right error number here. - schlpbch -
+
+ myPerlPath = new QString(KGlobal::dirs()->findExe("perl"));
+ if (myPerlPath->isEmpty())
+ {
+ //kdDebug() << "Perl command not found" << endl;
+ this->error(ERR_CANNOT_LAUNCH_PROCESS,
+ i18n("Could not find the Perl program on your system, please install."));
+ exit();
+ }
+ else
+ {
+ //kdDebug() << "Perl command found:" << *myPerlPath << endl;
+ }
+
+ myFingerPath = new QString(KGlobal::dirs()->findExe("finger"));
+ if ((myFingerPath->isEmpty()))
+ {
+ //kdDebug() << "Finger command not found" << endl;
+ this->error(ERR_CANNOT_LAUNCH_PROCESS,
+ i18n("Could not find the Finger program on your system, please install."));
+ exit();
+ }
+ else
+ {
+ //kdDebug() << "Finger command found:" << *myFingerPath << endl;
+ }
+
+ myFingerPerlScript = new QString(locate("data","kio_finger/kio_finger.pl"));
+ if (myFingerPerlScript->isEmpty())
+ {
+ //kdDebug() << "kio_finger.pl script not found" << endl;
+ this->error(ERR_CANNOT_LAUNCH_PROCESS,
+ i18n("kio_finger Perl script not found."));
+ exit();
+ }
+ else
+ {
+ //kdDebug() << "kio_finger perl script found: " << *myFingerPerlScript << endl;
+ }
+
+ myFingerCSSFile = new QString(locate("data","kio_finger/kio_finger.css"));
+ if (myFingerCSSFile->isEmpty())
+ {
+ //kdDebug() << "kio_finger.css file not found" << endl;
+ this->warning(i18n("kio_finger CSS script not found. Output will look ugly."));
+ }
+ else
+ {
+ //kdDebug() << "kio_finger CSS file found: " << *myFingerCSSFile << endl;
+ }
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+
+void FingerProtocol::parseCommandLine(const KURL& url)
+{
+ myURL = new KURL(url);
+
+ /*
+ * Generate a valid finger url
+ */
+
+ if(myURL->isEmpty() || !myURL->isValid() ||
+ (myURL->user().isEmpty() && myURL->host().isEmpty()))
+ {
+ myURL->setProtocol("finger");
+ myURL->setUser("");
+ myURL->setHost("localhost");
+ }
+
+ /*
+ * If no specific port is specified, set it to 79.
+ */
+
+ if(myURL->port() == 0) {
+ myURL->setPort(79);
+ }
+
+ /*
+ * If no refresh rate is given, set it to defaultRefreshRate
+ */
+
+ if (myURL->query().isEmpty()) {
+ myURL->setQuery("?refreshRate="+defaultRefreshRate);
+ }
+}
+
+/* ---------------------------------------------------------------------------------- */
+#include "kio_finger.moc"
+/* ---------------------------------------------------------------------------------- */
+
diff --git a/kioslave/finger/kio_finger.css b/kioslave/finger/kio_finger.css
new file mode 100644
index 000000000..06deb81aa
--- /dev/null
+++ b/kioslave/finger/kio_finger.css
@@ -0,0 +1,69 @@
+BODY {
+ color: #FFFFCC;
+ background-color: #000000;
+ padding: 2em;
+ margin: auto;
+}
+
+A:link {color: #82A7D0}
+A:visited {color: #999999}
+A:active {color: #999999}
+
+H1 {
+ color: #999999;
+ background-color: #000000;
+ font: 200% Helvetica, sans-serif;
+ font-variant: normal;
+ padding: 1em;
+ margin: auto;
+}
+
+.mainTable {
+ background-color: #000000;
+ border: thin solid;
+ margin: auto;
+}
+
+
+.courierText {
+ color: #FFFFCC;
+ background-color: #000000;
+ font: 120% Courier, sans-serif;
+ font-variant: normal;
+ text-align: left;
+ padding: 0em;
+}
+
+.commandText {
+ color: #FFFFCC;
+ background-color: #000000;
+ font: 120% Courier, sans-serif;
+ font-variant: normal;
+ text-align: center;
+ padding: 0.5em;
+}
+
+.niceText {
+ color: #009999;
+ background-color: #000000;
+ font: 120% Arial, sans-serif;
+ font-variant: normal;
+ text-align: center;
+ padding: 0.5em;
+}
+
+.finger { color: #82A7D0}
+.domainName { color: #D0A000}
+.ipNumber { color: #D0A000}
+.os { color: #82A7D0}
+.username { color: #82A7D0}
+.directory { color: #D0A000}
+.shell { color: #D0A000}
+.notLoggedIn { color: #00A000}
+.loggedIn { color: #B00000}
+.newMail { color: #82A7D0}
+.plan { color: #D0A000}
+.noNewMail { color: #BB0000}
+.noPlan { color: #BB0000}
+
+
diff --git a/kioslave/finger/kio_finger.h b/kioslave/finger/kio_finger.h
new file mode 100644
index 000000000..8d63236d4
--- /dev/null
+++ b/kioslave/finger/kio_finger.h
@@ -0,0 +1,64 @@
+
+/***************************************************************************
+ kio_finger.h - description
+ -------------------
+ begin : Sun Aug 12 2000
+ copyright : (C) 2000 by Andreas Schlapbach
+ email : schlpbch@iam.unibe.ch
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef __kio_finger_h__
+#define __kio_finger_h__
+
+#include <qstring.h>
+#include <qcstring.h>
+
+#include <kurl.h>
+#include <kprocess.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+class FingerProtocol : public QObject, public KIO::SlaveBase
+{
+ Q_OBJECT
+
+public:
+
+ FingerProtocol(const QCString &pool_socket, const QCString &app_socket);
+ virtual ~FingerProtocol();
+
+ virtual void mimetype(const KURL& url);
+ virtual void get(const KURL& url);
+
+private slots:
+ void slotGetStdOutput(KProcess*, char*, int);
+
+private:
+ KURL *myURL;
+
+ QString *myPerlPath;
+ QString *myFingerPath;
+ QString *myFingerPerlScript;
+ QString *myFingerCSSFile;
+
+ QString *myStdStream;
+
+
+ KProcess *myKProcess;
+
+ void getProgramPath();
+ void parseCommandLine(const KURL& url);
+};
+
+
+#endif
diff --git a/kioslave/finger/kio_finger.pl b/kioslave/finger/kio_finger.pl
new file mode 100644
index 000000000..8965ea523
--- /dev/null
+++ b/kioslave/finger/kio_finger.pl
@@ -0,0 +1,175 @@
+##!/usr/bin/perl
+#
+# Copyright Andreas Schlapbach, schlpbch@iam.unibe.ch, 2001
+# http://iamexwiwww.unibe.ch/studenten/kfinger
+#
+# Touch at your own risk.
+
+
+# Highlight mail addresses or url
+
+$mails = '<A HREF="mailto:';
+$urls = '<A HREF="';
+$urlspace = '">';
+$urlend = '</A>';
+
+# Highlight various information, configurable via the CSS file,
+
+$finger = '<CODE class="finger">';
+$domainName = '<CODE class="domainName">';
+$ipNumber = '<CODE class="ipNumber">';
+$os = '<CODE class="os">';
+$username = '<CODE class="username">';
+$directory = '<CODE class="directory">';
+$shell = '<CODE class="shell">';
+$notLoggedIn = '<CODE class="Login">';
+$loggedIn = '<CODE class="noLogin">';
+$newMail = '<CODE class="newMail">';
+$plan = '<CODE class="plan">';
+$noNewMail = '<CODE class="noNewMail">';
+$noPlan = '<CODE class="noPlan">';
+$close = '</CODE>';
+
+# Those names get skipped, so if there's a user with such a name, bad luck.
+
+@keywords=('Welcome','Login','finger','No');
+$keywordlist = join '|', @keywords;
+
+$FINGERCMD = "$ARGV[0]"; # The complete path to the finger cmd
+$CSSFILE = "$ARGV[1]"; # The complete path to the CSS file
+$REFRESHRATE = "$ARGV[2]"; # The intervals in seconds until the page gets updated
+$HOST = "$ARGV[3]"; # host name
+$USER = "$ARGV[4]"; # user name
+
+$HOST =~ s/&/&amp;/g;
+$HOST =~ s/</&lt;/g;
+$HOST =~ s/>/&gt;/g;
+
+$USER =~ s/&/&amp;/g;
+$USER =~ s/</&lt;/g;
+$USER =~ s/>/&gt;/g;
+
+# HTML Header
+
+print <<HTMLHeader;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <meta http-equiv="refresh" content="$REFRESHRATE">
+ <TITLE>finger $USER\@$HOST</TITLE>
+ <LINK type="text/css" rel="stylesheet" href="file:$CSSFILE">
+</HEAD>
+<BODY>
+ <TABLE class="mainTable" cellspacing="0">
+ <TR>
+ <TH colspan="1">
+ <H1>finger $USER\@$HOST</H1>
+ </TH>
+ </TR>
+ <TR>
+ <TH>
+ <TABLE class="courierText" cellpadding="0" cellspacing="2">
+HTMLHeader
+
+# Run finger command and save it into a buffer
+
+open(F, "-|") || exec $FINGERCMD, "$USER\@$HOST";
+@lines = <F>;
+close(F);
+
+# Do highlighting using perl regular expressions on every line received.
+# Order is important here.
+
+foreach $output (@lines)
+ {
+ $output =~ s/((\w)+\@((\w)+(.))*(\w)+)/$mails$1$urlspace$1$urlend/gi; # Highlight email address
+ $output =~ s/((http|ftp)(:\/\/)(\S)+)/$urls$1$urlspace$1$urlend/gi; # Highlight urls
+ $output =~ s/((\d)+\.(\d)+\.(\d)+\.(\d)+)/$ipNumber$1$close/gi; # Highlight IP number
+ $output =~ s/((\w)+\.(\w)+\.(\w|-)+\s)/$domainName$1$close/gi; # Highlight domain name (\s is important)
+ $output =~ s/(finger:)/$finger$1$close/gim; # Highlight finger
+ $output =~ s/(Linux)/$os$1$close/gim; # Highlight Linux
+ if ($USER) # is $USER nil ?
+ {
+ $output =~ s/^Login:\s*(\w*)/Login: $mails$1\@$HOST$urlspace$1$urlend/gi;
+ $output =~ s/^Login Name:\s*(\w*)/Login Name:$mails$1\@$HOST$urlspace$1$urlend/gi;
+ $output =~ s/Name:(((\s*)(\w+))+\n)/Name:$username$1$close\n/gi; # Linux
+ $output =~ s/In real life:(((\s*)(\w+))+\n)/In real life:$username$1$close\n/gi; # Solaris
+ $output =~ s/^Directory:((\s*)(\/(\w)+)+)/Directory:$directory$1$close/gi; # Highlight Directory
+ $output =~ s/Shell:((\s*)(\/(\w)+)+)/Shell:$shell$1$close/gi; # Highlight Shell
+ $output =~ s/(not presently logged)/$notLoggedIn$1$close/gi;
+ $output =~ s/con (\w*)/con $loggedIn$1$close/gi;
+ $output =~ s/^(New mail)/$newMail$1$close/gi;
+ $output =~ s/^(No mail.)/$noNewMail$1$close/gim;
+ $output =~ s/^(Plan:)/$plan$1$close/gi;
+ $output =~ s/^(No plan.)/$noPlan$1$close/gim;
+ }
+ else
+ {
+ $output =~ s/^(\w+)/$mails$1\@$HOST$urlspace$1$urlend/m unless ($output =~ m/$keywordlist/m);
+ }
+ # line consists of white space only?
+ if ($output =~ m/\S/gi)
+ {
+ print " <TR><TD><PRE>$output</PRE></TD></TR>\n";
+ }
+ else
+ {
+ print " <TR><TD><PRE> </PRE></TD></TR>\n";
+ }
+}
+
+print " </TABLE>\n";
+print " </TH>\n";
+
+# Finger-Talk options
+
+if ($USER) # is $USER nil ?
+{
+print <<UserQuery;
+ </TR>
+ <TR>
+ <TH class="commandText" colspan="2">
+ <A HREF='finger://$USER\@$HOST'>finger</A>
+ </TH>
+ </TR>
+UserQuery
+}
+else
+{
+print <<HostQueryHead;
+ <TH>
+ <TABLE class="courierText" cellpadding="0" cellspacing="2">
+HostQueryHead
+
+ @lines = split /^/m, $buffer;
+ foreach $output2 (@lines)
+ {
+ if ($output2 =~ m/^(\w+)/gi and not ($output2 =~ m/$keywordlist/m))
+ {
+ $USER = $&;
+ print " <TR><TD><PRE><A HREF='finger://$USER\@$HOST'>finger</A>\n</PRE></TD></TR>\n";
+ # - <A HREF='talk://$USER\@$HOST'>talk</A>\n</PRE></TD></TR>\n";
+ }
+ else
+ {
+ print " <TR><TD><PRE> </PRE></TD></TR>\n";
+ }
+ }
+
+print <<HostQueryTail;
+ </TABLE>
+ </TH>
+ </TR>
+HostQueryTail
+}
+
+# HTMLTail
+
+print <<HTMLTail;
+ <TR>
+ <TH class="niceText">refresh rate: $REFRESHRATE seconds.</TH>
+ </TR>
+</TABLE>
+</BODY>
+</HTML>
+HTMLTail
diff --git a/kioslave/fish/AUTHORS b/kioslave/fish/AUTHORS
new file mode 100644
index 000000000..fc4f1567e
--- /dev/null
+++ b/kioslave/fish/AUTHORS
@@ -0,0 +1 @@
+Jrg Walter <trouble@garni.ch>
diff --git a/kioslave/fish/COPYING b/kioslave/fish/COPYING
new file mode 100644
index 000000000..2d08eab44
--- /dev/null
+++ b/kioslave/fish/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/kioslave/fish/ChangeLog b/kioslave/fish/ChangeLog
new file mode 100644
index 000000000..7f621f3d4
--- /dev/null
+++ b/kioslave/fish/ChangeLog
@@ -0,0 +1,71 @@
+1.2.3 by Willy De la Court <willy.delacourt@pandora.be>
+ Changes in the EXEC code as Jrg Walter proposed.
+ fixed 2 bugs when executing in Shell mode
+
+1.2.2 by Willy De la Court <willy.delacourt@pandora.be>
+ Security fix tempfile should not be world readable
+ bugfix write to the file not the command
+
+1.2.1 by Willy De la Court <willy.delacourt@pandora.be>
+ implemented su for fish://localhost/
+ fish://root@localhost/ will su to root
+ fish://someuser@localhost/ will su to someuser
+ fish://localhost:22/ will still use ssh
+ strange problem with su when sending password need to wait a while (1 sec)
+ after reception of the password prompt.
+ some indentations fixed
+ i18n all messages
+
+1.2 by Willy De la Court <willy.delacourt@pandora.be>
+ implementation of the EXEC function
+ made sure all the VER lines where the same
+ used eval and system() for executing the command
+ Send the VER command directly after the FISH command.
+ After using kill to close the child process used wait to really make sure the child
+ has died. If the child took some time to die the select()
+ returns "Interrupted system call" error. This should solve some hanging problems.
+ added hasExec so it can be tested.
+ backport to BRANCH sendCommand(FISH_VER); and wait(NULL) to fix potential bugs.
+
+1.1.4 by Willy De la Court <willy.delacourt@pandora.be>
+ fixes Bug 49881: file time differs by 1 hour
+ and backported to BRANCH
+
+1.1.3
+ removed compression option, which fixes 2 bugs: #45448 and an
+ untracked user report about ssh version misdetect; also, is
+ more consistent with overall design policy: leave connection
+ details to the ssh client
+
+ fixed a bug which made lots of ssh zombie processes hang around
+
+1.1.2
+ fixed a bug which made inserting shell args via fish:-URL possible
+
+1.1.1
+ fixed shell mode symlink display
+
+ made perl server compatible with 5.005
+
+1.1
+ added a perl server implementation which is transferred
+ and started automatically if perl is present
+
+ added KDE3 support
+
+ added support for commercial SSH
+
+ modifed shell commands to use file(1)'s -i option (version
+ 3.37 and up) instead of local hack
+
+ fixed an annoying bug with copying/moving dir trees
+
+ fixed bug which made creating new files fail sometimes
+
+ added support for changing the user name in the pass dialog
+
+1.0.1
+ added #include <sys/resource.h> (needed on some platforms)
+
+1.0
+ initial release
diff --git a/kioslave/fish/FAQ b/kioslave/fish/FAQ
new file mode 100644
index 000000000..dce0aef41
--- /dev/null
+++ b/kioslave/fish/FAQ
@@ -0,0 +1,37 @@
+Freqeuently Asked Questions, last updated for kio_fish 1.1
+
+Q: Typing fish:/some.host.com does not work
+A: It is fish://some.host.com (double slash)
+
+Q: How can I use a different port?
+A: Use regular URL syntax: fish://some.host.com:2222
+
+Q: Something isn't working. I get strange/no displays
+A: Could be a bug, could be a problem with the remote
+ tools. Try having perl somewhere in the PATH on the
+ remote machine, that should work reliably. Shell-
+ only mode is prone to different tool's opinion about
+ parameters and what they mean. Shell-only mode is
+ thouroughly tested only on GNU tools, and has
+ superficial testing on BSD and Digital Unix. Solaris
+ seems to have problems. Any reports for shell mode on
+ non-GNU machines welcome (BTW, if you see a file
+ .fishsrv.pl in your remote home directory, fish did
+ use perl mode)
+
+Q: The connection stays open. How do I disconnect?
+A: Just wait. The system default idle timeout is used.
+ (about a minute or so)
+
+Q: Why are there no icons?
+A: With this release, you should have icons almost always,
+ but best results are obtained if you install a recent
+ version of the 'file' utility that supports the '-i'
+ option.
+
+Q: How do I specify which program to use for SSH?
+A: Not at all, sorry. After evaluating other programs (rsh,
+ rlogin, telnet) I came to the conclusion it was way too
+ complex to support these, as only ssh supports both,
+ password authentication and automatic execution.
+
diff --git a/kioslave/fish/INSTALL b/kioslave/fish/INSTALL
new file mode 100644
index 000000000..02a4a0740
--- /dev/null
+++ b/kioslave/fish/INSTALL
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 4. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/kioslave/fish/Makefile.am b/kioslave/fish/Makefile.am
new file mode 100644
index 000000000..27308245b
--- /dev/null
+++ b/kioslave/fish/Makefile.am
@@ -0,0 +1,33 @@
+kde_module_LTLIBRARIES = kio_fish.la
+
+INCLUDES = $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+kio_fish_la_SOURCES = fish.cpp
+kio_fish_la_LIBADD = $(LIB_KSYCOCA) #$(LIBUTIL)
+kio_fish_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+noinst_HEADERS = fishcode.h fish.h
+
+EXTRA_DIST = AUTHORS COPYING ChangeLog INSTALL README TODO FAQ fish.pl
+
+DISTCLEANFILES = fishcode.h
+
+kdelnk_DATA = fish.protocol nxfish.protocol
+kdelnkdir = $(kde_servicesdir)
+
+METASOURCES = AUTO
+
+fish.lo: fishcode.h
+
+fishcode.h: fish.pl
+ SUM=`$(MD5SUM) $(srcdir)/fish.pl | cut -d ' ' $(MD5SUM_CUT)`; \
+ echo '#define CHECKSUM "'$$SUM'"' > $@; \
+ echo 'static const char *fishCode(' >> $@; \
+ sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[ ]*$$/\\n"/;/^"\\n"$$/d;s/{CHECKSUM}/'$$SUM'/;' $(srcdir)/fish.pl >> $@; \
+ echo ');' >> $@;
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_fish.pot
+
+
+
diff --git a/kioslave/fish/README b/kioslave/fish/README
new file mode 100644
index 000000000..d1afdc3d1
--- /dev/null
+++ b/kioslave/fish/README
@@ -0,0 +1,258 @@
+Overview of kio_fish
+====================
+
+ ------------------------------------------------------------------------
+ NOTE FOR KDE2 USERS: This is the last release supporting KDE2. However,
+ you might need to modify Makefiles to get things installed into the
+ right directories.
+ ------------------------------------------------------------------------
+
+ FISH is a protocol to get filesystem access without special server
+ software, only using a remote shell. (Hence the name: FIles transferred
+ over SHell protocol).
+ It was first devised by Pavel Machek <pavel@bug.ucw.cz> and implemented
+ as a Midnight Commander vfs module in 1998.
+
+ This is a complete client implementation using his original version
+ 0.0.2 protocol, extending it with 2 commands (which are only optional -
+ should a real FISH server exist on server side that doesn't understand
+ them, this ioslave still works, only slower). Moreover, this client does
+ complete shell metacharacter quoting on all arguments, a fact that is
+ neccessary but missing from the specs.
+ Extensions used are: append (APPEND command), copy (COPY command),
+ lscount (LIST first prints number of files to be listed), lslinks (LIST
+ shows symlink info instead of info about link targets), lsmime (LIST
+ determines the MIME type on the server side)
+ Password and host key queries are handled via dialog boxes.
+ The goal of this client is to make a remote directory look and feel exactly
+ like a local directory, with all comfort, only slower.
+
+ NOTE: From version 1.1.3 on, compression is no longer turned on auto-
+ matically. You have to specify it via ~/.ssh/config or wherever
+ your local ssh client reads its settings. The same goes for all other
+ connection parameters. OpenSSH for example has a powerful configuration
+ file syntax which lets you configure access differently for each host,
+ something I do not intend to duplicate. Read the ssh_config(5) man page
+ for details. If someone knows the docs to read for commercial ssh please
+ tell me so I can include that here as well.
+
+ Included below is the original posting from the mc mailing list archives.
+
+ If perl is installed on the remote machine and in the default PATH, it will
+ be used to transfer a custom server script which is much faster than
+ shell-only mode and more predictable as well. The script is stored in a
+ file called .fishsrv.pl in the working directory directly after login and
+ will be reused on subsequent connections.
+
+ 2001/10/07 Jrg Walter <trouble@garni.ch>
+
+
+
+From: Pavel Machek <pavel@bug.ucw.cz>
+Subject: New virtual filesystem - fish
+Date: Tue, 15 Sep 1998 22:30:07 +0200
+
+Hi!
+
+New virtual filesystem has been created, which allows you to access
+files on remote computer over rsh/ssh connection, with _no_ server
+needed on the other side. To use it from mc or any program using
+libvfs.so, do
+
+cd /#sh:user@host.to.connect.to/
+
+Note that password authentication will not work, so you must be
+authenticated using [rs]hosts or RSA key.
+
+For protocol, see mc/vfs/README.fish. If someone wants to write
+server, it would be good idea, since it works without server but
+performance is not optimal.
+
+ Pavel
+
+PS: Protocol looks like this. If you have any comments, it is time to
+speak.
+
+
+ FIles transferred over SHell protocol (V 0.0.2)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This protocol was designed for transferring files over secureshell
+(ssh) connection. It can be as well used for transfers over rsh, and
+there may be other uses.
+
+Client sends requests of following form:
+
+#FISH_COMMAND
+equivalent shell commands,
+which may be multiline
+
+Only fish commands are defined here, shell equivalents are for your
+information only and will probably vary from implementation to
+implementation. Fish commands always have priority: server is
+expected to execute fish command if it understands it. If it does not,
+however, it can try the luck and execute shell command.
+
+Server's reply is multiline, but alwyas ends with
+
+### 000<optional text>
+
+line. ### is prefix to mark this line, 000 is return code. Return
+codes are superset to those used in ftp.
+
+There are few new exit codes defined:
+
+000 don't know; if there were no previous lines, this marks COMPLETE
+success, if they were, it marks failure.
+
+001 don't know; if there were no previous lines, this marks
+PRELIMinary success, if they were, it marks failure
+
+ Connecting
+ ~~~~~~~~~~
+Client uses "echo FISH:;/bin/sh" as command executed on remote
+machine. This should make it possible for server to distinguish FISH
+connections from normal rsh/ssh.
+
+ Commands
+ ~~~~~~~~
+#FISH
+echo; start_fish_server; echo '### 200'
+
+This command is sent at the begining. It marks that client wishes to
+talk via FISH protocol. #VER command must follow. If server
+understands FISH protocol, it has option to put FISH server somewhere
+on system path and name it start_fish_server.
+
+#VER 0.0.2 <feature1> <feature2> <...>
+echo '### 000'
+
+This command is the second one. It sends client version and extensions
+to the server. Server should reply with protocol version to be used,
+and list of extensions accepted.
+
+VER 0.0.0 <feature2>
+### 200
+
+#PWD
+pwd; echo '### 200'
+
+Server should reply with current directory (in form /abc/def/ghi)
+followed by line indicating success.
+
+#LIST /directory
+ls -lLa $1 | grep '^[^cbt]' | ( while read p x u g s m d y n; do echo "P$p $u.$g
+S$s
+d$m $d $y
+:$n
+"; done )
+ls -lLa $1 | grep '^[cb]' | ( while read p x u g a i m d y n; do echo "P$p $u.$g
+E$a$i
+dD$m $d $y
+:$n
+"; done )
+echo '### 200'
+
+This allows client to list directory or get status information about
+single file. Output is in following form (any line except :<filename>
+may be ommited):
+
+P<unix permissions> <owner>.<group>
+S<size>
+d<3-letters month name> <day> <year or HH:MM>
+D<year> <month> <day> <hour> <minute> <second>[.1234]
+E<major-of-device>,<minor>
+:<filename>
+L<filename symlink points to>
+<blank line to separate items>
+
+Unix permissions are of form X--------- where X is type of
+file. Currently, '-' means regular file, 'd' means directory, 'c', 'b'
+means character and block device, 'l' means symbolic link, 'p' means
+FIFO and 's' means socket.
+
+'d' has three fields: month (one of strings Jan Feb Mar Apr May Jun
+Jul Aug Sep Oct Nov Dec), day of month, and third is either single
+number indicating year, or HH:MM field (assume current year in such
+case). As you've probably noticed, this is pretty broken; it is for
+compatibility with ls listing.
+
+#RETR /some/name
+ls -l /some/name | ( read a b c d x e; echo $x ); echo '### 100'; cat /some/name; echo '### 200'
+
+Server sends line with filesize on it, followed by line with ### 100
+indicating partial success, then it sends binary data (exactly
+filesize bytes) and follows them with (with no preceeding newline) ###
+200.
+
+Note that there's no way to abort running RETR command - except
+closing the connection.
+
+#STOR <size> /file/name
+<i><font color="#008000">> /file/name; echo '### 001'; ( dd bs=4096 count=<size/4096>; dd bs=<size%4096> count=1 ) 2>/dev/null | ( cat > %s; cat > /dev/null ); echo '### 200'
+</font></i>
+This command is for storing /file/name, which is exactly size bytes
+big. You probably think I went crazy. Well, I did not: that strange
+cat > /dev/null has purpose to discard any extra data which was not
+written to disk (due to for example out of space condition).
+
+[Why? Imagine uploading file with "rm -rf /" line in it.]
+
+#CWD /somewhere
+cd /somewhere; echo '### 000'
+
+It is specified here, but I'm not sure how wise idea is to use this
+one: it breaks stateless-ness of the protocol.
+
+Following commands should be rather self-explanatory:
+
+#CHMOD 1234 file
+chmod 1234 file; echo '### 000'
+
+#DELE /some/path
+rm -f /some/path; echo '### 000'
+
+#MKD /some/path
+mkdir /some/path; echo '### 000'
+
+#RMD /some/path
+rmdir /some/path; echo '### 000'
+
+#RENAME /path/a /path/b
+mv /path/a /path/b; echo '### 000'
+
+#LINK /path/a /path/b
+ln /path/a /path/b; echo '### 000'
+
+#SYMLINK /path/a /path/b
+ln -s /path/a /path/b; echo '### 000'
+
+#CHOWN user /file/name
+chown user /file/name; echo '### 000'
+
+#CHGRP group /file/name
+chgrp group /file/name; echo '### 000'
+
+#READ <offset> <size> /path/and/filename
+cat /path/and/filename | ( dd bs=4096 count=<offset/4096> > /dev/null;
+dd bs=<offset%4096> count=1 > /dev/null;
+dd bs=4096 count=<offset/4096>;
+dd bs=<offset%4096> count=1; )
+
+Returns ### 200 on successfull exit, ### 291 on successfull exit when
+reading ended at eof, ### 292 on successfull exit when reading did not
+end at eof.
+
+#WRITE <offset> <size> /path/and/filename
+
+Hmm, shall we define these ones if we know our client is not going to
+use them?
+
+
+That's all, folks!
+ pavel@ucw.cz
+
+
+--
+I'm really pavel@atrey.karlin.mff.cuni.cz. Pavel
+Look at http://atrey.karlin.mff.cuni.cz/~pavel/ ;-).
diff --git a/kioslave/fish/TODO b/kioslave/fish/TODO
new file mode 100644
index 000000000..ba3bf69bb
--- /dev/null
+++ b/kioslave/fish/TODO
@@ -0,0 +1,10 @@
+L resume (could be very slow in shell mode due to WRITE being slow)
+L other remote shells (rlogin, telnet) - difficult, would need a shell prompt detector which is impossible to get 100% accurate. Contributions welcome.
+L show host list when called as fish://
+L plug into sidebar, show directory tree there
+M employ locking to only show one password dialog, so that loading many files at once from the same host would use a cahced password instead of opening tons of dialog boxes
+M more meaningful error messages (need perl server first)
+H use rsync or a similar technique (if possible at all)
+M proxying via intermediate ssh account
+H make it work with charsets other than latin1
+
diff --git a/kioslave/fish/configure.in.in b/kioslave/fish/configure.in.in
new file mode 100644
index 000000000..086dc0dae
--- /dev/null
+++ b/kioslave/fish/configure.in.in
@@ -0,0 +1,9 @@
+CFLAGS="$CFLAGS -D_GNU_SOURCE"
+CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE"
+
+AC_CHECK_HEADERS(termios.h pty.h libutil.h util.h sys/types.h sys/ioctl.h stropts.h)
+
+kde_save_LIBS="$LIBS"
+LIBS="$LIBS $LIBUTIL"
+AC_CHECK_FUNCS(getpt openpty isastream)
+LIBS="$kde_save_LIBS"
diff --git a/kioslave/fish/fish.cpp b/kioslave/fish/fish.cpp
new file mode 100644
index 000000000..3967bcd6b
--- /dev/null
+++ b/kioslave/fish/fish.cpp
@@ -0,0 +1,1661 @@
+/***************************************************************************
+ fish.cpp - a FISH kioslave
+ -------------------
+ begin : Thu Oct 4 17:09:14 CEST 2001
+ copyright : (C) 2001-2003 by J�rg Walter
+ email : jwalt-kde@garni.ch
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, version 2 of the License *
+ * *
+ ***************************************************************************/
+
+/*
+ This code contains fragments and ideas from the ftp kioslave
+ done by David Faure <faure@kde.org>.
+
+ Structure is a bit complicated, since I made the mistake to use
+ KProcess... now there is a lightweight homebrew async IO system
+ inside, but if signals/slots become available for ioslaves, switching
+ back to KProcess should be easy.
+*/
+
+#include "config.h"
+
+#include <qcstring.h>
+#include <qfile.h>
+#include <qsocket.h>
+#include <qdatetime.h>
+#include <qbitarray.h>
+#include <qregexp.h>
+
+#include <stdlib.h>
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#include <math.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#ifdef HAVE_STROPTS
+#include <stropts.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kremoteencoding.h>
+#include <kurl.h>
+#include <ksock.h>
+#include <stdarg.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <kmimetype.h>
+#include <kmimemagic.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <sys/resource.h>
+
+#include "fish.h"
+#include "fishcode.h"
+
+#ifndef NDEBUG
+#define myDebug(x) kdDebug(7127) << __LINE__ << ": " x
+#define connected() do{myDebug( << "_______ emitting connected()" << endl); connected();}while(0)
+#define dataReq() do{myDebug( << "_______ emitting dataReq()" << endl); dataReq();}while(0)
+#define needSubURLData() do{myDebug( << "_______ emitting needSubURLData()" << endl); needSubURLData();}while(0)
+#define slaveStatus(x,y) do{myDebug( << "_______ emitting slaveStatus(" << x << ", " << y << ")" << endl); slaveStatus(x,y);}while(0)
+#define statEntry(x) do{myDebug( << "_______ emitting statEntry("<<x.size()<<")" << endl); statEntry(x);}while(0)
+#define listEntries(x) do{myDebug( << "_______ emitting listEntries(...)" << endl); listEntries(x);}while(0)
+#define canResume(x) do{myDebug( << "_______ emitting canResume("<<(int)x<<")" << endl); canResume(x);}while(0)
+#define totalSize(x) do{myDebug( << "_______ emitting totalSize("<<(int)x<<")" << endl); totalSize(x);}while(0)
+#define processedSize(x) do{myDebug( << "_______ emitting processedSize("<<x<<")" << endl); processedSize(x);}while(0)
+#define speed(x) do{myDebug( << "_______ emitting speed("<<(int)x<<")" << endl); speed(x);}while(0)
+#define redirection(x) do{myDebug( << "_______ emitting redirection("<<x<<")" << endl); redirection(x);}while(0)
+#define errorPage() do{myDebug( << "_______ emitting errorPage()" << endl); errorPage();}while(0)
+#define sendmimeType(x) do{myDebug( << "_______ emitting mimeType("<<x<<")" << endl); mimeType(x);}while(0)
+#define warning(x) do{myDebug( << "_______ emitting warning("<<x<<")" << endl); warning(x);}while(0)
+#define infoMessage(x) do{myDebug( << "_______ emitting infoMessage("<<x<<")" << endl); infoMessage(x);}while(0)
+#else
+#define myDebug(x)
+#define sendmimeType(x) mimeType(x)
+#endif
+
+static char *sshPath = NULL;
+static char *suPath = NULL;
+// disabled: currently not needed. Didn't work reliably.
+// static int isOpenSSH = 0;
+
+static int isNXFish = 0;
+
+#define E(x) ((const char*)remoteEncoding()->encode(x).data())
+
+using namespace KIO;
+extern "C" {
+
+static void ripper(int)
+{
+ while (waitpid(-1,0,WNOHANG) > 0) {
+ // do nothing, go on
+ }
+}
+
+int KDE_EXPORT kdemain( int argc, char **argv )
+{
+ KLocale::setMainCatalogue("kio_fish");
+ KInstance instance("fish");
+
+ myDebug( << "*** Starting fish " << endl);
+ if (argc != 4) {
+ myDebug( << "Usage: fish protocol domain-socket1 domain-socket2" << endl);
+ exit(-1);
+ }
+
+ setenv("TZ", "UTC", true);
+
+ struct sigaction act;
+ memset(&act,0,sizeof(act));
+ act.sa_handler = ripper;
+ act.sa_flags = 0
+#ifdef SA_NOCLDSTOP
+ | SA_NOCLDSTOP
+#endif
+#ifdef SA_RESTART
+ | SA_RESTART
+#endif
+ ;
+ sigaction(SIGCHLD,&act,NULL);
+
+ if (qstrcmp(argv[1],"nxfish")==0) {
+ // Set NXFish - Mode
+ isNXFish=1;
+ }
+
+ fishProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ myDebug( << "*** fish Done" << endl);
+ return 0;
+}
+
+}
+
+const struct fishProtocol::fish_info fishProtocol::fishInfo[] = {
+ { ("FISH"), 0,
+ ("echo; /bin/sh -c start_fish_server > /dev/null 2>/dev/null; perl .fishsrv.pl " CHECKSUM " 2>/dev/null; perl -e '$|=1; print \"### 100 transfer fish server\\n\"; while(<STDIN>) { last if /^__END__/; $code.=$_; } exit(eval($code));' 2>/dev/null;"),
+ 1 },
+ { ("VER 0.0.3 copy append lscount lslinks lsmime exec stat"), 0,
+ ("echo 'VER 0.0.3 copy append lscount lslinks lsmime exec stat'"),
+ 1 },
+ { ("PWD"), 0,
+ ("pwd"),
+ 1 },
+ { ("LIST"), 1,
+ ("echo `ls -Lla %1 2> /dev/null | grep '^[-dsplcb]' | wc -l`; ls -Lla %1 2>/dev/null | grep '^[-dspl]' | ( while read -r p x u g s m d y n; do file -b -i $n 2>/dev/null | sed -e '\\,^[^/]*$,d;s/^/M/;s,/.*[ \t],/,'; FILE=%1; if [ -e %1\"/$n\" ]; then FILE=%1\"/$n\"; fi; if [ -L \"$FILE\" ]; then echo \":$n\"; ls -lad \"$FILE\" | sed -e 's/.* -> /L/'; else echo \":$n\" | sed -e 's/ -> /\\\nL/'; fi; echo \"P$p $u.$g\nS$s\nd$m $d $y\n\"; done; );"
+ "ls -Lla %1 2>/dev/null | grep '^[cb]' | ( while read -r p x u g a i m d y n; do echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"; done; )"),
+ 0 },
+ { ("STAT"), 1,
+ ("echo `ls -dLla %1 2> /dev/null | grep '^[-dsplcb]' | wc -l`; ls -dLla %1 2>/dev/null | grep '^[-dspl]' | ( while read -r p x u g s m d y n; do file -b -i $n 2>/dev/null | sed -e '\\,^[^/]*$,d;s/^/M/;s,/.*[ \t],/,'; FILE=%1; if [ -e %1\"/$n\" ]; then FILE=%1\"/$n\"; fi; if [ -L \"$FILE\" ]; then echo \":$n\"; ls -lad \"$FILE\" | sed -e 's/.* -> /L/'; else echo \":$n\" | sed -e 's/ -> /\\\nL/'; fi; echo \"P$p $u.$g\nS$s\nd$m $d $y\n\"; done; );"
+ "ls -dLla %1 2>/dev/null | grep '^[cb]' | ( while read -r p x u g a i m d y n; do echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"; done; )"),
+ 0 },
+ { ("RETR"), 1,
+ ("ls -l %1 2>&1 | ( read -r a b c d x e; echo $x ) 2>&1; echo '### 001'; cat %1"),
+ 1 },
+ { ("STOR"), 2,
+ ("> %2; echo '### 001'; ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` 2>/dev/null;"
+ "[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 2>/dev/null; ) | ( cat > %2 || echo Error $?; cat > /dev/null )"),
+ 0 },
+ { ("CWD"), 1,
+ ("cd %1"),
+ 0 },
+ { ("CHMOD"), 2,
+ ("chmod %1 %2"),
+ 0 },
+ { ("DELE"), 1,
+ ("rm -f %1"),
+ 0 },
+ { ("MKD"), 1,
+ ("mkdir %1"),
+ 0 },
+ { ("RMD"), 1,
+ ("rmdir %1"),
+ 0 },
+ { ("RENAME"), 2,
+ ("mv -f %1 %2"),
+ 0 },
+ { ("LINK"), 2,
+ ("ln -f %1 %2"),
+ 0 },
+ { ("SYMLINK"), 2,
+ ("ln -sf %1 %2"),
+ 0 },
+ { ("CHOWN"), 2,
+ ("chown %1 %2"),
+ 0 },
+ { ("CHGRP"), 2,
+ ("chgrp %1 %2"),
+ 0 },
+ { ("READ"), 3,
+ ("echo '### 100';cat %3 /dev/zero | ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` >/dev/null;"
+ "[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 >/dev/null;"
+ "dd bs=%2 count=1; ) 2>/dev/null;"),
+ 0 },
+ // Yes, this is "ibs=1", since dd "count" is input blocks.
+ // On network connections, read() may not fill the buffer
+ // completely (no more data immediately available), but dd
+ // does ignore that fact by design. Sorry, writes are slow.
+ // OTOH, WRITE is not used by the current ioslave methods,
+ // we use APPEND.
+ { ("WRITE"), 3,
+ (">> %3; echo '### 001'; ( [ %2 -gt 0 ] && dd ibs=1 obs=%2 count=%2 2>/dev/null ) | "
+ "( dd ibs=32768 obs=%1 seek=1 of=%3 2>/dev/null || echo Error $?; cat >/dev/null; )"),
+ 0 },
+ { ("COPY"), 2,
+ ("if [ -L %1 ]; then if cp -pdf %1 %2 2>/dev/null; then :; else LINK=\"`readlink %1`\"; ln -sf $LINK %2; fi; else cp -pf %1 %2; fi"),
+ 0 },
+ { ("APPEND"), 2,
+ (">> %2; echo '### 001'; ( [ %1 -gt 0 ] && dd ibs=1 obs=%1 count=%1 2> /dev/null; ) | ( cat >> %2 || echo Error $?; cat >/dev/null; )"),
+ 0 },
+ { ("EXEC"), 2,
+ ("UMASK=`umask`; umask 077; touch %2; umask $UMASK; eval %1 < /dev/null > %2 2>&1; echo \"###RESULT: $?\" >> %2"),
+ 0 }
+};
+
+fishProtocol::fishProtocol(const QCString &pool_socket, const QCString &app_socket)
+ : SlaveBase("fish", pool_socket, app_socket), mimeBuffer(1024),
+ mimeTypeSent(false)
+{
+ myDebug( << "fishProtocol::fishProtocol()" << endl);
+ if (sshPath == NULL) {
+ // disabled: currently not needed. Didn't work reliably.
+ // isOpenSSH = !system("ssh -V 2>&1 | grep OpenSSH > /dev/null");
+ if (isNXFish)
+ sshPath = strdup(QFile::encodeName(KStandardDirs::findExe("nxfish")));
+ else
+ sshPath = strdup(QFile::encodeName(KStandardDirs::findExe("ssh")));
+ }
+ if (suPath == NULL) {
+ suPath = strdup(QFile::encodeName(KStandardDirs::findExe("su")));
+ }
+ childPid = 0;
+ connectionPort = 0;
+ isLoggedIn = false;
+ writeReady = true;
+ isRunning = false;
+ firstLogin = true;
+ errorCount = 0;
+ rawRead = 0;
+ rawWrite = -1;
+ recvLen = -1;
+ sendLen = -1;
+ setMultipleAuthCaching( true );
+ connectionAuth.keepPassword = true;
+ connectionAuth.url.setProtocol("fish");
+ outBufPos = -1;
+ outBuf = NULL;
+ outBufLen = 0;
+ typeAtom.m_uds = UDS_FILE_TYPE;
+ typeAtom.m_long = 0;
+ mimeAtom.m_uds = UDS_MIME_TYPE;
+ mimeAtom.m_long = 0;
+ mimeAtom.m_str = QString::null;
+
+ hasAppend = false;
+
+ isStat = false; // FIXME: just a workaround for konq deficiencies
+ redirectUser = ""; // FIXME: just a workaround for konq deficiencies
+ redirectPass = ""; // FIXME: just a workaround for konq deficiencies
+ fishCodeLen = strlen(fishCode);
+}
+/* ---------------------------------------------------------------------------------- */
+
+
+fishProtocol::~fishProtocol()
+{
+ myDebug( << "fishProtocol::~fishProtocol()" << endl);
+ shutdownConnection(true);
+}
+
+/* --------------------------------------------------------------------------- */
+
+/**
+Connects to a server and logs us in via SSH. Then starts FISH protocol.
+*/
+void fishProtocol::openConnection() {
+ if (childPid) return;
+
+ if (connectionHost.isEmpty() && !isNXFish)
+ {
+ error( KIO::ERR_UNKNOWN_HOST, QString::null );
+ return;
+ }
+
+ infoMessage(i18n("Connecting..."));
+
+ myDebug( << "connecting to: " << connectionUser << "@" << connectionHost << ":" << connectionPort << endl);
+ sendCommand(FISH_FISH);
+ sendCommand(FISH_VER);
+ if (connectionStart()) {
+ error(ERR_COULD_NOT_CONNECT,connectionHost);
+ shutdownConnection();
+ return;
+ };
+ myDebug( << "subprocess is running" << endl);
+}
+
+static int open_pty_pair(int fd[2])
+{
+#if defined(HAVE_TERMIOS_H) && defined(HAVE_GRANTPT) && !defined(HAVE_OPENPTY)
+/** with kind regards to The GNU C Library
+Reference Manual for Version 2.2.x of the GNU C Library */
+ int master, slave;
+ char *name;
+ struct ::termios ti;
+ memset(&ti,0,sizeof(ti));
+
+ ti.c_cflag = CLOCAL|CREAD|CS8;
+ ti.c_cc[VMIN] = 1;
+
+#ifdef HAVE_GETPT
+ master = getpt();
+#else
+ master = open("/dev/ptmx", O_RDWR);
+#endif
+ if (master < 0) return 0;
+
+ if (grantpt(master) < 0 || unlockpt(master) < 0) goto close_master;
+
+ name = ptsname(master);
+ if (name == NULL) goto close_master;
+
+ slave = open(name, O_RDWR);
+ if (slave == -1) goto close_master;
+
+#if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
+ if (isastream(slave) &&
+ (ioctl(slave, I_PUSH, "ptem") < 0 ||
+ ioctl(slave, I_PUSH, "ldterm") < 0))
+ goto close_slave;
+#endif
+
+ tcsetattr(slave, TCSANOW, &ti);
+ fd[0] = master;
+ fd[1] = slave;
+ return 0;
+
+#if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
+close_slave:
+#endif
+ close(slave);
+
+close_master:
+ close(master);
+ return -1;
+#else
+#ifdef HAVE_OPENPTY
+ struct ::termios ti;
+ memset(&ti,0,sizeof(ti));
+
+ ti.c_cflag = CLOCAL|CREAD|CS8;
+ ti.c_cc[VMIN] = 1;
+
+ return openpty(fd,fd+1,NULL,&ti,NULL);
+#else
+#ifdef __GNUC__
+#warning "No tty support available. Password dialog won't work."
+#endif
+ return socketpair(PF_UNIX,SOCK_STREAM,0,fd);
+#endif
+#endif
+}
+/**
+creates the subprocess
+*/
+bool fishProtocol::connectionStart() {
+ int fd[2];
+ int rc, flags;
+ thisFn = QString::null;
+
+ rc = open_pty_pair(fd);
+ if (rc == -1) {
+ myDebug( << "socketpair failed, error: " << strerror(errno) << endl);
+ return true;
+ }
+
+ if (!requestNetwork()) return true;
+ myDebug( << "Exec: " << (local ? suPath : sshPath) << " Port: " << connectionPort << " User: " << connectionUser << endl);
+ childPid = fork();
+ if (childPid == -1) {
+ myDebug( << "fork failed, error: " << strerror(errno) << endl);
+ close(fd[0]);
+ close(fd[1]);
+ childPid = 0;
+ dropNetwork();
+ return true;
+ }
+ if (childPid == 0) {
+ // taken from konsole, see TEPty.C for details
+ // note: if we're running on socket pairs,
+ // this will fail, but thats what we expect
+
+ for (int sig = 1; sig < NSIG; sig++) signal(sig,SIG_DFL);
+
+ struct rlimit rlp;
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ for (int i = 0; i < (int)rlp.rlim_cur; i++)
+ if (i != fd[1]) close(i);
+
+ dup2(fd[1],0);
+ dup2(fd[1],1);
+ dup2(fd[1],2);
+ if (fd[1] > 2) close(fd[1]);
+
+ setsid();
+
+#if defined(TIOCSCTTY)
+ ioctl(0, TIOCSCTTY, 0);
+#endif
+
+ int pgrp = getpid();
+#if defined( _AIX) || defined( __hpux)
+ tcsetpgrp(0, pgrp);
+#else
+ ioctl(0, TIOCSPGRP, (char *)&pgrp);
+#endif
+
+ const char *dev = ttyname(0);
+ setpgid(0,0);
+ if (dev) close(open(dev, O_WRONLY, 0));
+ setpgid(0,0);
+
+ if (local) {
+ execl(suPath, "su", "-", connectionUser.latin1(), "-c", "cd ~;echo FISH:;exec /bin/sh -c \"if env true 2>/dev/null; then env PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; else PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; fi\"", (void *)0);
+ } else {
+ #define common_args "-l", connectionUser.latin1(), "-x", "-e", "none", \
+ "-q", connectionHost.latin1(), \
+ "echo FISH:;exec /bin/sh -c \"if env true 2>/dev/null; then env PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; else PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; fi\"", (void *)0
+ // disabled: leave compression up to the client.
+ // (isOpenSSH?"-C":"+C"),
+
+ if (connectionPort)
+ execl(sshPath, "ssh", "-p", QString::number(connectionPort).latin1(), common_args);
+ else
+ execl(sshPath, "ssh", common_args);
+ #undef common_args
+ }
+ myDebug( << "could not exec! " << strerror(errno) << endl);
+ ::exit(-1);
+ }
+ close(fd[1]);
+ rc = fcntl(fd[0],F_GETFL,&flags);
+ rc = fcntl(fd[0],F_SETFL,flags|O_NONBLOCK);
+ childFd = fd[0];
+
+ fd_set rfds, wfds;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ char buf[32768];
+ int offset = 0;
+ while (!isLoggedIn) {
+ FD_SET(childFd,&rfds);
+ FD_ZERO(&wfds);
+ if (outBufPos >= 0) FD_SET(childFd,&wfds);
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1000;
+ rc = select(childFd+1, &rfds, &wfds, NULL, &timeout);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "select failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ return true;
+ }
+ if (FD_ISSET(childFd,&wfds) && outBufPos >= 0) {
+ if (outBuf) rc = write(childFd,outBuf+outBufPos,outBufLen-outBufPos);
+ else rc = 0;
+ if (rc >= 0) outBufPos += rc;
+ else {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "write failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ outBufPos = -1;
+ //return true;
+ }
+ if (outBufPos >= outBufLen) {
+ outBufPos = -1;
+ outBuf = NULL;
+ outBufLen = 0;
+ }
+ }
+ if (FD_ISSET(childFd,&rfds)) {
+ rc = read(childFd,buf+offset,32768-offset);
+ if (rc > 0) {
+ int noff = establishConnection(buf,rc+offset);
+ if (noff < 0) return false;
+ if (noff > 0) memmove(buf,buf+offset+rc-noff,noff);
+ offset = noff;
+ } else {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "read failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+writes one chunk of data to stdin of child process
+*/
+void fishProtocol::writeChild(const char *buf, KIO::fileoffset_t len) {
+ if (outBufPos >= 0 && outBuf) {
+#if 0
+ QString debug;
+ debug.setLatin1(outBuf,outBufLen);
+ if (len > 0) myDebug( << "write request while old one is pending, throwing away input (" << outBufLen << "," << outBufPos << "," << debug.left(10) << "...)" << endl);
+#endif
+ return;
+ }
+ outBuf = buf;
+ outBufPos = 0;
+ outBufLen = len;
+}
+
+/**
+manages initial communication setup including password queries
+*/
+int fishProtocol::establishConnection(char *buffer, KIO::fileoffset_t len) {
+ QString buf;
+ buf.setLatin1(buffer,len);
+ int pos;
+ // Strip trailing whitespace
+ while (buf.length() && (buf[buf.length()-1] == ' '))
+ buf.truncate(buf.length()-1);
+
+ myDebug( << "establishing: got " << buf << endl);
+ while (childPid && ((pos = buf.find('\n')) >= 0 ||
+ buf.endsWith(":") || buf.endsWith("?"))) {
+ pos++;
+ QString str = buf.left(pos);
+ buf = buf.mid(pos);
+ if (str == "\n")
+ continue;
+ if (str == "FISH:\n") {
+ thisFn = QString::null;
+ infoMessage(i18n("Initiating protocol..."));
+ if (!connectionAuth.password.isEmpty()) {
+ connectionAuth.password = connectionAuth.password.left(connectionAuth.password.length()-1);
+ cacheAuthentication(connectionAuth);
+ }
+ isLoggedIn = true;
+ return 0;
+ } else if (!str.isEmpty()) {
+ thisFn += str;
+ } else if (buf.endsWith(":")) {
+ if (!redirectUser.isEmpty() && connectionUser != redirectUser) {
+ KURL dest = url;
+ dest.setUser(redirectUser);
+ dest.setPass(redirectPass);
+ redirection(dest);
+ commandList.clear();
+ commandCodes.clear();
+ finished();
+ redirectUser = "";
+ redirectPass = "";
+ return -1;
+ } else if (!connectionPassword.isEmpty()) {
+ myDebug( << "sending cpass" << endl);
+ connectionAuth.password = connectionPassword+"\n";
+ connectionPassword = QString::null;
+ // su does not like receiving a password directly after sending
+ // the password prompt so we wait a while.
+ if (local)
+ sleep(1);
+ writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
+ } else {
+ myDebug( << "sending mpass" << endl);
+ connectionAuth.prompt = thisFn+buf;
+ if (local)
+ connectionAuth.caption = i18n("Local Login") + " - " + url.user() + "@" + url.host();
+ else
+ connectionAuth.caption = i18n("SSH Authorization") + " - " + url.user() + "@" + url.host();
+ if ((!firstLogin || !checkCachedAuthentication(connectionAuth))) {
+ connectionAuth.password = QString::null; // don't prefill
+ if ( !openPassDlg(connectionAuth)) {
+ error(ERR_USER_CANCELED,connectionHost);
+ shutdownConnection();
+ return -1;
+ }
+ }
+ firstLogin = false;
+ connectionAuth.password += "\n";
+ if (connectionAuth.username != connectionUser) {
+ KURL dest = url;
+ dest.setUser(connectionAuth.username);
+ dest.setPass(connectionAuth.password);
+ redirection(dest);
+ if (isStat) { // FIXME: just a workaround for konq deficiencies
+ redirectUser = connectionAuth.username;
+ redirectPass = connectionAuth.password;
+ }
+ commandList.clear();
+ commandCodes.clear();
+ finished();
+ return -1;
+ }
+ myDebug( << "sending pass" << endl);
+ if (local)
+ sleep(1);
+ writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
+ }
+ thisFn = QString::null;
+ return 0;
+ } else if (buf.endsWith("?")) {
+ int rc = messageBox(QuestionYesNo,thisFn+buf);
+ if (rc == KMessageBox::Yes) {
+ writeChild("yes\n",4);
+ } else {
+ writeChild("no\n",3);
+ }
+ thisFn = QString::null;
+ return 0;
+ } else {
+ myDebug( << "unmatched case in initial handling! shouldn't happen!" << endl);
+ }
+ }
+ return buf.length();
+}
+/**
+sets connection information for subsequent commands
+*/
+void fishProtocol::setHost(const QString & host, int port, const QString & u, const QString & pass){
+ QString user(u);
+
+ if (isNXFish)
+ local = 0;
+ else
+ local = (host == "localhost" && port == 0);
+
+ if (port <= 0) port = 0;
+ if (user.isEmpty()) user = getenv("LOGNAME");
+
+ if (host == connectionHost && port == connectionPort && user == connectionUser)
+ return;
+ myDebug( << "setHost " << u << "@" << host << endl);
+
+ if (childPid) shutdownConnection();
+
+ connectionHost = host;
+ connectionAuth.url.setHost(host);
+
+ connectionUser = user;
+ connectionAuth.username = user;
+ connectionAuth.url.setUser(user);
+
+ connectionPort = port;
+ connectionPassword = pass;
+ firstLogin = true;
+}
+
+/**
+Forced close of the connection
+
+This function gets called from the application side of the universe,
+it shouldn't send any response.
+ */
+void fishProtocol::closeConnection(){
+ myDebug( << "closeConnection()" << endl);
+ shutdownConnection(true);
+}
+
+/**
+Closes the connection
+ */
+void fishProtocol::shutdownConnection(bool forced){
+ if (childPid) {
+ kill(childPid,SIGTERM); // We may not have permission...
+ childPid = 0;
+ close(childFd); // ...in which case this should do the trick
+ childFd = -1;
+ if (!forced)
+ {
+ dropNetwork();
+ infoMessage(i18n("Disconnected."));
+ }
+ }
+ outBufPos = -1;
+ outBuf = NULL;
+ outBufLen = 0;
+ qlist.clear();
+ commandList.clear();
+ commandCodes.clear();
+ isLoggedIn = false;
+ writeReady = true;
+ isRunning = false;
+ rawRead = 0;
+ rawWrite = -1;
+ recvLen = -1;
+ sendLen = -1;
+}
+/**
+builds each FISH request and sets the error counter
+*/
+bool fishProtocol::sendCommand(fish_command_type cmd, ...) {
+ const fish_info &info = fishInfo[cmd];
+ myDebug( << "queueing: cmd="<< cmd << "['" << info.command << "'](" << info.params <<"), alt=['" << info.alt << "'], lines=" << info.lines << endl);
+
+ va_list list;
+ va_start(list, cmd);
+ QString realCmd = info.command;
+ QString realAlt = info.alt;
+ static QRegExp rx("[][\\\\\n $`#!()*?{}~&<>;'\"%^@|\t]");
+ for (int i = 0; i < info.params; i++) {
+ QString arg(va_arg(list, const char *));
+ int pos = -2;
+ while ((pos = rx.search(arg,pos+2)) >= 0) {
+ arg.replace(pos,0,QString("\\"));
+ }
+ //myDebug( << "arg " << i << ": " << arg << endl);
+ realCmd.append(" ").append(arg);
+ realAlt.replace(QRegExp("%"+QString::number(i+1)),arg);
+ }
+ QString s("#");
+ s.append(realCmd).append("\n ").append(realAlt).append(" 2>&1;echo '### 000'\n");
+ if (realCmd == "FISH")
+ s.prepend(" ");
+ commandList.append(s);
+ commandCodes.append(cmd);
+ return true;
+}
+
+/**
+checks response string for result code, converting 000 and 001 appropriately
+*/
+int fishProtocol::handleResponse(const QString &str){
+ myDebug( << "handling: " << str << endl);
+ if (str.startsWith("### ")) {
+ bool isOk = false;
+ int result = str.mid(4,3).toInt(&isOk);
+ if (!isOk) result = 500;
+ if (result == 0) result = (errorCount != 0?500:200);
+ if (result == 1) result = (errorCount != 0?500:100);
+ myDebug( << "result: " << result << ", errorCount: " << errorCount << endl);
+ return result;
+ } else {
+ errorCount++;
+ return 0;
+ }
+}
+
+int fishProtocol::makeTimeFromLs(const QString &monthStr, const QString &dayStr, const QString &timeyearStr)
+{
+ QDateTime dt(QDate::currentDate(Qt::UTC));
+ int year = dt.date().year();
+ int month = dt.date().month();
+ int currentMonth = month;
+ int day = dayStr.toInt();
+
+ static const char * const monthNames[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ for (int i=0; i < 12; i++) if (monthStr.startsWith(monthNames[i])) {
+ month = i+1;
+ break;
+ }
+
+ int pos = timeyearStr.find(':');
+ if (timeyearStr.length() == 4 && pos == -1) {
+ year = timeyearStr.toInt();
+ } else if (pos == -1) {
+ return 0;
+ } else {
+ if (month > currentMonth + 1) year--;
+ dt.time().setHMS(timeyearStr.left(pos).toInt(),timeyearStr.mid(pos+1).toInt(),0);
+ }
+ dt.date().setYMD(year,month,day);
+
+ return dt.toTime_t();
+}
+
+/**
+parses response from server and acts accordingly
+*/
+void fishProtocol::manageConnection(const QString &l) {
+ QString line(l);
+ int rc = handleResponse(line);
+ UDSAtom atom;
+ QDateTime dt;
+ KIO::filesize_t fsize;
+ int pos, pos2, pos3;
+ bool isOk = false;
+ if (!rc) {
+ switch (fishCommand) {
+ case FISH_VER:
+ if (line.startsWith("VER 0.0.3")) {
+ line.append(" ");
+ hasAppend = line.contains(" append ");
+ } else {
+ error(ERR_UNSUPPORTED_PROTOCOL,line);
+ shutdownConnection();
+ }
+ break;
+ case FISH_PWD:
+ url.setPath(line);
+ redirection(url);
+ break;
+ case FISH_LIST:
+ myDebug( << "listReason: " << listReason << endl);
+ /* Fall through */
+ case FISH_STAT:
+ if (line.length() > 0) {
+ switch (line[0].cell()) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ fsize = line.toULongLong(&isOk);
+ if (fsize > 0 && isOk) errorCount--;
+ if ((fishCommand == FISH_LIST) && (listReason == LIST))
+ totalSize(fsize);
+ break;
+
+ case 'P':
+ errorCount--;
+ if (line[1] == 'd') {
+ mimeAtom.m_str = "inode/directory";
+ typeAtom.m_long = S_IFDIR;
+ } else {
+ if (line[1] == '-') {
+ typeAtom.m_long = S_IFREG;
+ } else if (line[1] == 'l') {
+ typeAtom.m_long = S_IFLNK;
+ } else if (line[1] == 'c') {
+ typeAtom.m_long = S_IFCHR;
+ } else if (line[1] == 'b') {
+ typeAtom.m_long = S_IFBLK;
+ } else if (line[1] == 's') {
+ typeAtom.m_long = S_IFSOCK;
+ } else if (line[1] == 'p') {
+ typeAtom.m_long = S_IFIFO;
+ } else {
+ myDebug( << "unknown file type: " << line[1].cell() << endl);
+ errorCount++;
+ break;
+ }
+ }
+ //myDebug( << "file type: " << atom.m_long << endl);
+ //udsEntry.append(atom);
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = 0;
+ if (line[2] == 'r') atom.m_long |= S_IRUSR;
+ if (line[3] == 'w') atom.m_long |= S_IWUSR;
+ if (line[4] == 'x' || line[4] == 's') atom.m_long |= S_IXUSR;
+ if (line[4] == 'S' || line[4] == 's') atom.m_long |= S_ISUID;
+ if (line[5] == 'r') atom.m_long |= S_IRGRP;
+ if (line[6] == 'w') atom.m_long |= S_IWGRP;
+ if (line[7] == 'x' || line[7] == 's') atom.m_long |= S_IXGRP;
+ if (line[7] == 'S' || line[7] == 's') atom.m_long |= S_ISGID;
+ if (line[8] == 'r') atom.m_long |= S_IROTH;
+ if (line[9] == 'w') atom.m_long |= S_IWOTH;
+ if (line[10] == 'x' || line[10] == 't') atom.m_long |= S_IXOTH;
+ if (line[10] == 'T' || line[10] == 't') atom.m_long |= S_ISVTX;
+ udsEntry.append(atom);
+
+ atom.m_uds = UDS_USER;
+ atom.m_long = 0;
+ pos = line.find('.',12);
+ if (pos < 0) {
+ errorCount++;
+ break;
+ }
+ atom.m_str = line.mid(12,pos-12);
+ udsEntry.append(atom);
+
+ atom.m_uds = UDS_GROUP;
+ atom.m_long = 0;
+ atom.m_str = line.mid(pos+1);
+ udsEntry.append(atom);
+ break;
+
+ case 'd':
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ pos = line.find(' ');
+ pos2 = line.find(' ',pos+1);
+ if (pos < 0 || pos2 < 0) break;
+ errorCount--;
+ atom.m_long = makeTimeFromLs(line.mid(1,pos-1), line.mid(pos+1,pos2-pos), line.mid(pos2+1));
+ udsEntry.append(atom);
+ break;
+
+ case 'D':
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ pos = line.find(' ');
+ pos2 = line.find(' ',pos+1);
+ pos3 = line.find(' ',pos2+1);
+ if (pos < 0 || pos2 < 0 || pos3 < 0) break;
+ dt.setDate(QDate(line.mid(1,pos-1).toInt(),line.mid(pos+1,pos2-pos-1).toInt(),line.mid(pos2+1,pos3-pos2-1).toInt()));
+ pos = pos3;
+ pos2 = line.find(' ',pos+1);
+ pos3 = line.find(' ',pos2+1);
+ if (pos < 0 || pos2 < 0 || pos3 < 0) break;
+ dt.setTime(QTime(line.mid(pos+1,pos2-pos-1).toInt(),line.mid(pos2+1,pos3-pos2-1).toInt(),line.mid(pos3+1).toInt()));
+ errorCount--;
+ atom.m_long = dt.toTime_t();
+ udsEntry.append(atom);
+ break;
+
+ case 'S':
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = line.mid(1).toULongLong(&isOk);
+ if (!isOk) break;
+ errorCount--;
+ udsEntry.append(atom);
+ break;
+
+ case 'E':
+ errorCount--;
+ break;
+
+ case ':':
+ atom.m_uds = UDS_NAME;
+ atom.m_long = 0;
+ pos = line.findRev('/');
+ atom.m_str = thisFn = line.mid(pos < 0?1:pos+1);
+ if (fishCommand == FISH_LIST)
+ udsEntry.append(atom);
+ // By default, the mimetype comes from the extension
+ // We'll use the file(1) result only as fallback [like the rest of KDE does]
+ {
+ KURL kurl("fish://host/");
+ kurl.setFileName(thisFn); // properly encode special chars
+ KMimeType::Ptr mime = KMimeType::findByURL(kurl);
+ if ( mime->name() != KMimeType::defaultMimeType() )
+ mimeAtom.m_str = mime->name();
+ }
+ errorCount--;
+ break;
+
+ case 'M': {
+ QString type = line.mid(1);
+
+ // First thing's first. If remote says this is a directory, throw out any
+ // name-based file type guesses.
+ if (type == "inode/directory" && mimeAtom.m_str != type) {
+ mimeAtom.m_str = type;
+ typeAtom.m_long = S_IFDIR;
+ }
+ // This is getting ugly. file(1) makes some uneducated
+ // guesses, so we must try to ignore them (#51274)
+ else if (mimeAtom.m_str.isEmpty() && line.right(8) != "/unknown" &&
+ (thisFn.find('.') < 0 || (line.left(8) != "Mtext/x-"
+ && line != "Mtext/plain"))) {
+ mimeAtom.m_str = type;
+ }
+ errorCount--;
+ break;
+ }
+
+ case 'L':
+ atom.m_uds = UDS_LINK_DEST;
+ atom.m_long = 0;
+ atom.m_str = line.mid(1);
+ udsEntry.append(atom);
+ if (!typeAtom.m_long) typeAtom.m_long = S_IFLNK;
+ errorCount--;
+ break;
+ }
+ } else {
+ if (!mimeAtom.m_str.isNull())
+ udsEntry.append(mimeAtom);
+ mimeAtom.m_str = QString::null;
+
+ udsEntry.append(typeAtom);
+ typeAtom.m_long = 0;
+
+ if (fishCommand == FISH_STAT)
+ udsStatEntry = udsEntry;
+ else if (listReason == LIST) {
+ listEntry(udsEntry, false); //1
+ } else if (listReason == CHECK) checkExist = true; //0
+ errorCount--;
+ udsEntry.clear();
+ }
+ break;
+
+ case FISH_RETR:
+ if (line.length() == 0) {
+ error(ERR_IS_DIRECTORY,url.prettyURL());
+ recvLen = 0;
+ break;
+ }
+ recvLen = line.toLongLong(&isOk);
+ if (!isOk) {
+ error(ERR_COULD_NOT_READ,url.prettyURL());
+ shutdownConnection();
+ break;
+ }
+ break;
+ default : break;
+ }
+
+ } else if (rc == 100) {
+ switch (fishCommand) {
+ case FISH_FISH:
+ writeChild(fishCode, fishCodeLen);
+ break;
+ case FISH_READ:
+ recvLen = 1024;
+ /* fall through */
+ case FISH_RETR:
+ myDebug( << "reading " << recvLen << endl);
+ if (recvLen == -1) {
+ error(ERR_COULD_NOT_READ,url.prettyURL());
+ shutdownConnection();
+ } else {
+ rawRead = recvLen;
+ dataRead = 0;
+ mimeTypeSent = false;
+ if (recvLen == 0)
+ {
+ mimeType("application/x-zerosize");
+ mimeTypeSent = true;
+ }
+ }
+ break;
+ case FISH_STOR:
+ case FISH_WRITE:
+ case FISH_APPEND:
+ rawWrite = sendLen;
+ //myDebug( << "sending " << sendLen << endl);
+ writeChild(NULL,0);
+ break;
+ default : break;
+ }
+ } else if (rc/100 != 2) {
+ switch (fishCommand) {
+ case FISH_STOR:
+ case FISH_WRITE:
+ case FISH_APPEND:
+ error(ERR_COULD_NOT_WRITE,url.prettyURL());
+ shutdownConnection();
+ break;
+ case FISH_RETR:
+ error(ERR_COULD_NOT_READ,url.prettyURL());
+ shutdownConnection();
+ break;
+ case FISH_READ:
+ if ( rc == 501 )
+ {
+ mimeType("inode/directory");
+ mimeTypeSent = true;
+ recvLen = 0;
+ finished();
+ }
+ else
+ {
+ error(ERR_COULD_NOT_READ,url.prettyURL());
+ shutdownConnection();
+ }
+ break;
+ case FISH_FISH:
+ case FISH_VER:
+ error(ERR_SLAVE_DEFINED,line);
+ shutdownConnection();
+ break;
+ case FISH_PWD:
+ case FISH_CWD:
+ error(ERR_CANNOT_ENTER_DIRECTORY,url.prettyURL());
+ break;
+ case FISH_LIST:
+ myDebug( << "list error. reason: " << listReason << endl);
+ if (listReason == LIST) error(ERR_CANNOT_ENTER_DIRECTORY,url.prettyURL());
+ else if (listReason == CHECK) {
+ checkExist = false;
+ finished();
+ }
+ break;
+ case FISH_STAT:
+ error(ERR_DOES_NOT_EXIST,url.prettyURL());
+ udsStatEntry.clear();
+ break;
+ case FISH_CHMOD:
+ error(ERR_CANNOT_CHMOD,url.prettyURL());
+ break;
+ case FISH_CHOWN:
+ case FISH_CHGRP:
+ error(ERR_ACCESS_DENIED,url.prettyURL());
+ break;
+ case FISH_MKD:
+ if ( rc == 501 )
+ error(ERR_DIR_ALREADY_EXIST,url.prettyURL());
+ else
+ error(ERR_COULD_NOT_MKDIR,url.prettyURL());
+ break;
+ case FISH_RMD:
+ error(ERR_COULD_NOT_RMDIR,url.prettyURL());
+ break;
+ case FISH_DELE:
+ error(ERR_CANNOT_DELETE,url.prettyURL());
+ break;
+ case FISH_RENAME:
+ error(ERR_CANNOT_RENAME,url.prettyURL());
+ break;
+ case FISH_COPY:
+ case FISH_LINK:
+ case FISH_SYMLINK:
+ error(ERR_COULD_NOT_WRITE,url.prettyURL());
+ break;
+ default : break;
+ }
+ } else {
+ if (fishCommand == FISH_STOR) fishCommand = (hasAppend?FISH_APPEND:FISH_WRITE);
+ if (fishCommand == FISH_FISH) {
+ connected();
+ } else if (fishCommand == FISH_LIST) {
+ if (listReason == LIST) {
+ listEntry(UDSEntry(),true);
+ } else if (listReason == CHECK) {
+ if (!checkOverwrite && checkExist)
+ {
+ error(ERR_FILE_ALREADY_EXIST,url.prettyURL());
+ return; // Don't call finished!
+ }
+ }
+ } else if (fishCommand == FISH_STAT) {
+ UDSAtom atom;
+
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = url.fileName();
+ udsStatEntry.append( atom );
+ statEntry(udsStatEntry);
+ } else if (fishCommand == FISH_APPEND) {
+ dataReq();
+ if (readData(rawData) > 0) sendCommand(FISH_APPEND,E(QString::number(rawData.size())),E(url.path()));
+ else if (!checkExist && putPerm > -1) sendCommand(FISH_CHMOD,E(QString::number(putPerm,8)),E(url.path()));
+ sendLen = rawData.size();
+ } else if (fishCommand == FISH_WRITE) {
+ dataReq();
+ if (readData(rawData) > 0) sendCommand(FISH_WRITE,E(QString::number(putPos)),E(QString::number(rawData.size())),E(url.path()));
+ else if (!checkExist && putPerm > -1) sendCommand(FISH_CHMOD,E(QString::number(putPerm,8)),E(url.path()));
+ putPos += rawData.size();
+ sendLen = rawData.size();
+ } else if (fishCommand == FISH_RETR) {
+ data(QByteArray());
+ }
+ finished();
+ }
+}
+
+void fishProtocol::writeStdin(const QString &line)
+{
+ qlist.append(line);
+
+ if (writeReady) {
+ writeReady = false;
+ //myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().find('\n')) << endl);
+ myDebug( << "Writing: " << qlist.first() << endl);
+ myDebug( << "---------" << endl);
+ writeChild((const char *)qlist.first().latin1(), qlist.first().length());
+ }
+}
+
+void fishProtocol::sent()
+{
+ if (rawWrite > 0) {
+ myDebug( << "writing raw: " << rawData.size() << "/" << rawWrite << endl);
+ writeChild(rawData.data(),(rawWrite > rawData.size()?rawData.size():rawWrite));
+ rawWrite -= rawData.size();
+ if (rawWrite > 0) {
+ dataReq();
+ if (readData(rawData) <= 0) {
+ shutdownConnection();
+ }
+ }
+ return;
+ } else if (rawWrite == 0) {
+ // workaround: some dd's insist in reading multiples of
+ // 8 bytes, swallowing up to seven bytes. Sending
+ // newlines is safe even when a sane dd is used
+ writeChild("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",15);
+ rawWrite = -1;
+ return;
+ }
+ if (qlist.count() > 0) qlist.remove(qlist.begin());
+ if (qlist.count() == 0) {
+ writeReady = true;
+ } else {
+ //myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().find('\n')) << endl);
+ myDebug( << "Writing: " << qlist.first() << endl);
+ myDebug( << "---------" << endl);
+ writeChild((const char *)qlist.first().latin1(),qlist.first().length());
+ }
+}
+
+int fishProtocol::received(const char *buffer, KIO::fileoffset_t buflen)
+{
+ int pos = 0;
+ do {
+ if (buflen <= 0) break;
+
+ if (rawRead > 0) {
+ //myDebug( << "processedSize " << dataRead << ", len " << buflen << "/" << rawRead << endl);
+ int dataSize = (rawRead > buflen?buflen:rawRead);
+ if (!mimeTypeSent)
+ {
+ int mimeSize = QMIN(dataSize, (int)mimeBuffer.size()-dataRead);
+ memcpy(mimeBuffer.data()+dataRead,buffer,mimeSize);
+ dataRead += mimeSize;
+ rawRead -= mimeSize;
+ buffer += mimeSize;
+ buflen -= mimeSize;
+ if (rawRead == 0) // End of data
+ mimeBuffer.resize(dataRead);
+ if (dataRead < (int)mimeBuffer.size())
+ {
+ myDebug( << "wait for more" << endl);
+ break;
+ }
+
+ // We need a KMimeType::findByNameAndContent(filename,data)
+ // For now we do: find by extension, and if not found (or extension not reliable)
+ // then find by content.
+ bool accurate = false;
+ KMimeType::Ptr mime = KMimeType::findByURL( url, 0, false, true, &accurate );
+ if ( !mime || mime->name() == KMimeType::defaultMimeType()
+ || !accurate )
+ {
+ KMimeType::Ptr p_mimeType = KMimeType::findByContent(mimeBuffer);
+ if ( p_mimeType && p_mimeType->name() != KMimeType::defaultMimeType() )
+ mime = p_mimeType;
+ }
+
+ sendmimeType(mime->name());
+
+
+ mimeTypeSent = true;
+ if (fishCommand != FISH_READ) {
+ totalSize(dataRead + rawRead);
+ data(mimeBuffer);
+ processedSize(dataRead);
+ }
+ mimeBuffer.resize(1024);
+ pos = 0;
+ continue; // Process rest of buffer/buflen
+ }
+
+ QByteArray bdata;
+ bdata.duplicate(buffer,dataSize);
+ data(bdata);
+
+ dataRead += dataSize;
+ rawRead -= dataSize;
+ processedSize(dataRead);
+ if (rawRead <= 0) {
+ buffer += dataSize;
+ buflen -= dataSize;
+ } else {
+ return 0;
+ }
+ }
+
+ if (buflen <= 0) break;
+
+ pos = 0;
+ // Find newline
+ while((pos < buflen) && (buffer[pos] != '\n'))
+ ++pos;
+
+ if (pos < buflen)
+ {
+ QString s = remoteEncoding()->decode(QCString(buffer,pos+1));
+
+ buffer += pos+1;
+ buflen -= pos+1;
+
+ manageConnection(s);
+
+ pos = 0;
+ // Find next newline
+ while((pos < buflen) && (buffer[pos] != '\n'))
+ ++pos;
+ }
+ } while (childPid && buflen && (rawRead > 0 || pos < buflen));
+ return buflen;
+}
+/** get a file */
+void fishProtocol::get(const KURL& u){
+ myDebug( << "@@@@@@@@@ get " << u << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ recvLen = -1;
+ sendCommand(FISH_RETR,E(url.path()));
+ }
+ run();
+}
+
+/** put a file */
+void fishProtocol::put(const KURL& u, int permissions, bool overwrite, bool /*resume*/){
+ myDebug( << "@@@@@@@@@ put " << u << " " << permissions << " " << overwrite << " " /* << resume */ << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ putPerm = permissions;
+ checkOverwrite = overwrite;
+ checkExist = false;
+ putPos = 0;
+ listReason = CHECK;
+ sendCommand(FISH_LIST,E(url.path()));
+ sendCommand(FISH_STOR,"0",E(url.path()));
+ }
+ run();
+}
+/** executes next command in sequence or calls finished() if all is done */
+void fishProtocol::finished() {
+ if (commandList.count() > 0) {
+ fishCommand = (fish_command_type)commandCodes.first();
+ errorCount = -fishInfo[fishCommand].lines;
+ rawRead = 0;
+ rawWrite = -1;
+ udsEntry.clear();
+ udsStatEntry.clear();
+ writeStdin(commandList.first());
+ //if (fishCommand != FISH_APPEND && fishCommand != FISH_WRITE) infoMessage("Sending "+(commandList.first().mid(1,commandList.first().find("\n")-1))+"...");
+ commandList.remove(commandList.begin());
+ commandCodes.remove(commandCodes.begin());
+ } else {
+ myDebug( << "_______ emitting finished()" << endl);
+ SlaveBase::finished();
+ isRunning = false;
+ }
+}
+/** aborts command sequence and calls error() */
+void fishProtocol::error(int type, const QString &detail) {
+ commandList.clear();
+ commandCodes.clear();
+ myDebug( << "ERROR: " << type << " - " << detail << endl);
+ SlaveBase::error(type,detail);
+ isRunning = false;
+}
+/** executes a chain of commands */
+void fishProtocol::run() {
+ if (!isRunning) {
+ int rc;
+ isRunning = true;
+ finished();
+ fd_set rfds, wfds;
+ FD_ZERO(&rfds);
+ char buf[32768];
+ int offset = 0;
+ while (isRunning) {
+ FD_SET(childFd,&rfds);
+ FD_ZERO(&wfds);
+ if (outBufPos >= 0) FD_SET(childFd,&wfds);
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1000;
+ rc = select(childFd+1, &rfds, &wfds, NULL, &timeout);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "select failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ error(ERR_CONNECTION_BROKEN,connectionHost);
+ shutdownConnection();
+ return;
+ }
+ if (FD_ISSET(childFd,&wfds) && outBufPos >= 0) {
+#if 0
+ QString debug;
+ debug.setLatin1(outBuf+outBufPos,outBufLen-outBufPos);
+ myDebug( << "now writing " << (outBufLen-outBufPos) << " " << debug.left(40) << "..." << endl);
+#endif
+ if (outBufLen-outBufPos > 0) rc = write(childFd,outBuf+outBufPos,outBufLen-outBufPos);
+ else rc = 0;
+ if (rc >= 0) outBufPos += rc;
+ else {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "write failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ error(ERR_CONNECTION_BROKEN,connectionHost);
+ shutdownConnection();
+ return;
+ }
+ if (outBufPos >= outBufLen) {
+ outBufPos = -1;
+ outBuf = NULL;
+ sent();
+ }
+ }
+ else if (FD_ISSET(childFd,&rfds)) {
+ rc = read(childFd,buf+offset,32768-offset);
+ //myDebug( << "read " << rc << " bytes" << endl);
+ if (rc > 0) {
+ int noff = received(buf,rc+offset);
+ if (noff > 0) memmove(buf,buf+offset+rc-noff,noff);
+ //myDebug( << "left " << noff << " bytes: " << QString::fromLatin1(buf,offset) << endl);
+ offset = noff;
+ } else {
+ if (errno == EINTR)
+ continue;
+ myDebug( << "read failed, rc: " << rc << ", error: " << strerror(errno) << endl);
+ error(ERR_CONNECTION_BROKEN,connectionHost);
+ shutdownConnection();
+ return;
+ }
+ }
+ if (wasKilled())
+ return;
+ }
+ }
+}
+/** stat a file */
+void fishProtocol::stat(const KURL& u){
+ myDebug( << "@@@@@@@@@ stat " << u << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ isStat = true; // FIXME: just a workaround for konq deficiencies
+ openConnection();
+ isStat = false; // FIXME: just a workaround for konq deficiencies
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ sendCommand(FISH_STAT,E(url.path(-1)));
+ }
+ run();
+}
+/** find mimetype for a file */
+void fishProtocol::mimetype(const KURL& u){
+ myDebug( << "@@@@@@@@@ mimetype " << u << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ recvLen = 1024;
+ sendCommand(FISH_READ,"0","1024",E(url.path()));
+ }
+ run();
+}
+/** list a directory */
+void fishProtocol::listDir(const KURL& u){
+ myDebug( << "@@@@@@@@@ listDir " << u << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ listReason = LIST;
+ sendCommand(FISH_LIST,E(url.path()));
+ }
+ run();
+}
+/** create a directory */
+void fishProtocol::mkdir(const KURL& u, int permissions) {
+ myDebug( << "@@@@@@@@@ mkdir " << u << " " << permissions << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ sendCommand(FISH_MKD,E(url.path()));
+ if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
+ }
+ run();
+}
+/** rename a file */
+void fishProtocol::rename(const KURL& s, const KURL& d, bool overwrite) {
+ myDebug( << "@@@@@@@@@ rename " << s << " " << d << " " << overwrite << endl);
+ if (s.host() != d.host() || s.port() != d.port() || s.user() != d.user()) {
+ error(ERR_UNSUPPORTED_ACTION,s.prettyURL());
+ return;
+ }
+ setHost(s.host(),s.port(),s.user(),s.pass());
+ url = d;
+ openConnection();
+ if (!isLoggedIn) return;
+ KURL src = s;
+ url.cleanPath();
+ src.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ if (!overwrite) {
+ listReason = CHECK;
+ checkOverwrite = false;
+ sendCommand(FISH_LIST,E(url.path()));
+ }
+ sendCommand(FISH_RENAME,E(src.path()),E(url.path()));
+ }
+ run();
+}
+/** create a symlink */
+void fishProtocol::symlink(const QString& target, const KURL& u, bool overwrite) {
+ myDebug( << "@@@@@@@@@ symlink " << target << " " << u << " " << overwrite << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ if (!overwrite) {
+ listReason = CHECK;
+ checkOverwrite = false;
+ sendCommand(FISH_LIST,E(url.path()));
+ }
+ sendCommand(FISH_SYMLINK,E(target),E(url.path()));
+ }
+ run();
+}
+/** change file permissions */
+void fishProtocol::chmod(const KURL& u, int permissions){
+ myDebug( << "@@@@@@@@@ chmod " << u << " " << permissions << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
+ }
+ run();
+}
+/** copies a file */
+void fishProtocol::copy(const KURL &s, const KURL &d, int permissions, bool overwrite) {
+ myDebug( << "@@@@@@@@@ copy " << s << " " << d << " " << permissions << " " << overwrite << endl);
+ if (s.host() != d.host() || s.port() != d.port() || s.user() != d.user()) {
+ error(ERR_UNSUPPORTED_ACTION,s.prettyURL());
+ return;
+ }
+ //myDebug( << s << endl << d << endl);
+ setHost(s.host(),s.port(),s.user(),s.pass());
+ url = d;
+ openConnection();
+ if (!isLoggedIn) return;
+ KURL src = s;
+ url.cleanPath();
+ src.cleanPath();
+ if (!src.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ if (!overwrite) {
+ listReason = CHECK;
+ checkOverwrite = false;
+ sendCommand(FISH_LIST,E(url.path()));
+ }
+ sendCommand(FISH_COPY,E(src.path()),E(url.path()));
+ if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
+ }
+ run();
+}
+/** removes a file or directory */
+void fishProtocol::del(const KURL &u, bool isFile){
+ myDebug( << "@@@@@@@@@ del " << u << " " << isFile << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ url.cleanPath();
+ if (!url.hasPath()) {
+ sendCommand(FISH_PWD);
+ } else {
+ sendCommand((isFile?FISH_DELE:FISH_RMD),E(url.path()));
+ }
+ run();
+}
+/** special like background execute */
+void fishProtocol::special( const QByteArray &data ){
+ int tmp;
+
+ QDataStream stream(data, IO_ReadOnly);
+
+ stream >> tmp;
+ switch (tmp) {
+ case FISH_EXEC_CMD: // SSH EXEC
+ {
+ KURL u;
+ QString command;
+ QString tempfile;
+ stream >> u;
+ stream >> command;
+ myDebug( << "@@@@@@@@@ exec " << u << " " << command << endl);
+ setHost(u.host(),u.port(),u.user(),u.pass());
+ url = u;
+ openConnection();
+ if (!isLoggedIn) return;
+ sendCommand(FISH_EXEC,E(command),E(url.path()));
+ run();
+ break;
+ }
+ default:
+ // Some command we don't understand.
+ error(ERR_UNSUPPORTED_ACTION,QString().setNum(tmp));
+ break;
+ }
+}
+/** report status */
+void fishProtocol::slave_status() {
+ myDebug( << "@@@@@@@@@ slave_status" << endl);
+ if (childPid > 0)
+ slaveStatus(connectionHost,isLoggedIn);
+ else
+ slaveStatus(QString::null,false);
+}
diff --git a/kioslave/fish/fish.h b/kioslave/fish/fish.h
new file mode 100644
index 000000000..e2665a320
--- /dev/null
+++ b/kioslave/fish/fish.h
@@ -0,0 +1,211 @@
+/***************************************************************************
+ fish.h - a FISH kioslave
+ -------------------
+ begin : Thu Oct 4 17:09:14 CEST 2001
+ copyright : (C) 2001 by Jrg Walter
+ email : trouble@garni.ch
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, version 2 of the License *
+ * *
+ ***************************************************************************/
+#ifndef __fish_h__
+#define __fish_h__
+
+#include <qstring.h>
+#include <qcstring.h>
+
+
+#include <kurl.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+#include <kprocess.h>
+#include <kio/authinfo.h>
+#include <time.h>
+
+#define FISH_EXEC_CMD 'X'
+
+class fishProtocol : public KIO::SlaveBase
+{
+public:
+ fishProtocol(const QCString &pool_socket, const QCString &app_socket);
+ virtual ~fishProtocol();
+
+ /**
+Connects to a server and logs us in via SSH. Then starts FISH protocol.
+@ref isConnected is set to true if logging on was successful.
+It is set to false if the connection becomes closed.
+
+ */
+ void openConnection();
+
+ /**
+ Clean up connection
+ */
+ void shutdownConnection(bool forced=false);
+ /** sets connection information for subsequent commands */
+ void setHost(const QString & host, int port, const QString & user, const QString & pass);
+ /** Forced close of the connection */
+ void closeConnection();
+ /** get a file */
+ void get(const KURL& url);
+ /** put a file */
+ void put(const KURL& url, int permissions, bool overwrite, bool resume);
+ /** aborts command sequence and calls error() */
+ void error(int type, const QString &detail);
+ /** executes next command in sequence or calls finished() if all is done */
+ void finished();
+ /** stat a file */
+ void stat(const KURL& url);
+ /** find mimetype for a file */
+ void mimetype(const KURL& url);
+ /** list a directory */
+ void listDir(const KURL& url);
+ /** create a directory */
+ void mkdir(const KURL&url, int permissions);
+ /** rename a file */
+ void rename(const KURL& src, const KURL& dest, bool overwrite);
+ /** create a symlink */
+ void symlink(const QString& target, const KURL& dest, bool overwrite);
+ /** change file permissions */
+ void chmod(const KURL& url, int permissions);
+ /** copies a file */
+ void copy(const KURL &src, const KURL &dest, int permissions, bool overwrite);
+ /** report status */
+ void slave_status();
+ /** removes a file or directory */
+ void del(const KURL &u, bool isfile);
+ /** special like background execute */
+ void special( const QByteArray &data );
+
+private: // Private attributes
+ /** the SSH process used to communicate with the remote end */
+ pid_t childPid;
+ /** fd for reading and writing to the process */
+ int childFd;
+ /** buffer for data to be written */
+ const char *outBuf;
+ /** current write position in buffer */
+ KIO::fileoffset_t outBufPos;
+ /** length of buffer */
+ KIO::fileoffset_t outBufLen;
+ /** use su if true else use ssh */
+ bool local;
+ /** // FIXME: just a workaround for konq deficiencies */
+ bool isStat;
+ /** // FIXME: just a workaround for konq deficiencies */
+ QString redirectUser, redirectPass;
+
+protected: // Protected attributes
+ /** for LIST/STAT */
+ KIO::UDSEntry udsEntry;
+ /** for LIST/STAT */
+ KIO::UDSEntry udsStatEntry;
+ /** for LIST/STAT */
+ KIO::UDSAtom typeAtom;
+ /** for LIST/STAT */
+ KIO::UDSAtom mimeAtom;
+ /** for LIST/STAT */
+ QString thisFn;
+ /** for STAT */
+ QString wantedFn;
+ QString statPath;
+ /** url of current request */
+ KURL url;
+ /** true if connection is logged in successfully */
+ bool isLoggedIn;
+ /** host name of current connection */
+ QString connectionHost;
+ /** user name of current connection */
+ QString connectionUser;
+ /** port of current connection */
+ int connectionPort;
+ /** password of current connection */
+ QString connectionPassword;
+ /** AuthInfo object used for logging in */
+ KIO::AuthInfo connectionAuth;
+ /** number of lines received, == 0 -> everything went ok */
+ int errorCount;
+ /** queue for lines to be sent */
+ QStringList qlist;
+ /** queue for commands to be sent */
+ QStringList commandList;
+ /** queue for commands to be sent */
+ QValueList<int> commandCodes;
+ /** bytes still to be read in raw mode */
+ KIO::fileoffset_t rawRead;
+ /** bytes still to be written in raw mode */
+ KIO::fileoffset_t rawWrite;
+ /** data bytes to read in next read command */
+ KIO::fileoffset_t recvLen;
+ /** data bytes to write in next write command */
+ KIO::fileoffset_t sendLen;
+ /** true if the last write operation was finished */
+ bool writeReady;
+ /** true if a command stack is currently executing */
+ bool isRunning;
+ /** reason of LIST command */
+ enum { CHECK, LIST } listReason;
+ /** true if FISH server understands APPEND command */
+ bool hasAppend;
+ /** permission of created file */
+ int putPerm;
+ /** true if file may be overwritten */
+ bool checkOverwrite;
+ /** current position of write */
+ KIO::fileoffset_t putPos;
+ /** true if file already existed */
+ bool checkExist;
+ /** true if this is the first login attempt (== use cached password) */
+ bool firstLogin;
+ /** write buffer */
+ QByteArray rawData;
+ /** buffer for storing bytes used for MimeMagic */
+ QByteArray mimeBuffer;
+ /** whther the mimetype has been sent already */
+ bool mimeTypeSent;
+ /** number of bytes read so far */
+ KIO::fileoffset_t dataRead;
+ /** details about each fishCommand */
+ static const struct fish_info {
+ const char *command;
+ int params;
+ const char *alt;
+ int lines;
+ } fishInfo[];
+ /** last FISH command sent to server */
+ enum fish_command_type { FISH_FISH, FISH_VER, FISH_PWD, FISH_LIST, FISH_STAT,
+ FISH_RETR, FISH_STOR,
+ FISH_CWD, FISH_CHMOD, FISH_DELE, FISH_MKD, FISH_RMD,
+ FISH_RENAME, FISH_LINK, FISH_SYMLINK, FISH_CHOWN,
+ FISH_CHGRP, FISH_READ, FISH_WRITE, FISH_COPY, FISH_APPEND, FISH_EXEC } fishCommand;
+ int fishCodeLen;
+protected: // Protected methods
+ /** manages initial communication setup including password queries */
+ int establishConnection(char *buffer, KIO::fileoffset_t buflen);
+ int received(const char *buffer, KIO::fileoffset_t buflen);
+ void sent();
+ /** builds each FISH request and sets the error counter */
+ bool sendCommand(fish_command_type cmd, ...);
+ /** checks response string for result code, converting 000 and 001 appropriately */
+ int handleResponse(const QString &str);
+ /** parses a ls -l time spec */
+ int makeTimeFromLs(const QString &dayStr, const QString &monthStr, const QString &timeyearStr);
+ /** executes a chain of commands */
+ void run();
+ /** creates the subprocess */
+ bool connectionStart();
+ /** writes one chunk of data to stdin of child process */
+ void writeChild(const char *buf, KIO::fileoffset_t len);
+ /** parses response from server and acts accordingly */
+ void manageConnection(const QString &line);
+ /** writes to process */
+ void writeStdin(const QString &line);
+};
+
+
+#endif
diff --git a/kioslave/fish/fish.pl b/kioslave/fish/fish.pl
new file mode 100755
index 000000000..1ba539f9f
--- /dev/null
+++ b/kioslave/fish/fish.pl
@@ -0,0 +1,369 @@
+#!/usr/bin/perl
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 2 of the License
+=pod
+This file was transferred by kio_fish, a network client part of the
+KDE project. You may safely delete it, it will be transferred again
+when needed. It's only purpose is to make kio_fish access faster and
+more reliable.
+=cut
+
+use Fcntl;
+
+$|++;
+#open(DEBUG,">/tmp/kio_fish.debug.$$.log");
+# save code in initial directory if just transferred
+if (defined $code) {
+ unlink('.fishsrv.pl');
+ sysopen(FH,'.fishsrv.pl',O_WRONLY|O_CREAT|O_EXCL);
+ print FH $code;
+ close(FH);
+ chmod(0444,'.fishsrv.pl');
+# request new code if it changed (checksum mismatch)
+# for automatic upgrades
+} elsif ($ARGV[0] ne "{CHECKSUM}") {
+ $|=1;
+ print "### 100 transfer fish server\n";
+ while(<STDIN>) {
+ last if /^__END__/;
+ $code.=$_;
+ }
+ exit(eval($code));
+}
+
+# we are up and running.
+print "### 200\n";
+use strict;
+use POSIX qw(getcwd dup2 strftime);
+$SIG{'CHLD'} = 'IGNORE';
+$| = 1;
+MAIN: while (<STDIN>) {
+ chomp;
+ chomp;
+ next if !length($_) || substr($_,0,1) ne '#';
+#print DEBUG "$_\n";
+ s/^#//;
+ /^VER / && do {
+ # We do not advertise "append" capability anymore, as "write" is
+ # as fast in perl mode and more reliable (overlapping writes)
+ print "VER 0.0.3 copy lscount lslinks lsmime exec stat\n### 200\n";
+ next;
+ };
+ /^PWD$/ && do {
+ print getcwd(),"\n### 200\n";
+ next;
+ };
+ /^SYMLINK\s+((?:\\.|[^\\])*?)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $ofn = unquote($1);
+ my $fn = unquote($2);
+ print (symlink($ofn,$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^COPY\s+((?:\\.|[^\\])*?)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $ofn = unquote($1);
+ my $fn = unquote($2);
+ my ($size) = (stat($ofn))[7];
+ my $read = 1;
+ if (-l $ofn) {
+ my $dest = readlink($ofn);
+ unlink($fn);
+ symlink($dest,$fn) || ($read = 0);
+ } else {
+ sysopen(FH,$ofn,O_RDONLY) || do { print "### 500 $!\n"; next; };
+ sysopen(OFH,$fn,O_WRONLY|O_CREAT|O_TRUNC) || do { close(FH); print "### 500 $!\n"; next; };
+ local $/ = undef;
+ my $buffer = '';
+ while ($size > 32768 && ($read = sysread(FH,$buffer,32768)) > 0) {
+ $size -= $read;
+ if (syswrite(OFH,$buffer,$read) != $read) {
+ close(FH); close(OFH);
+ print "### 500 $!\n";
+ next MAIN;
+ }
+
+ }
+ while ($size > 0 && ($read = sysread(FH,$buffer,$size)) > 0) {
+ $size -= $read;
+ if (syswrite(OFH,$buffer,$read) != $read) {
+ close(FH); close(OFH);
+ print "### 500 $!\n";
+ next MAIN;
+ }
+ }
+ close(FH);
+ close(OFH);
+ }
+ if ($read > 0) {
+ print "### 200\n";
+ } else {
+ print "### 500 $!\n";
+ }
+ next;
+ };
+ /^LINK\s+((?:\\.|[^\\])*?)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $ofn = unquote($1);
+ my $fn = unquote($2);
+ print (link($ofn,$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^RENAME\s+((?:\\.|[^\\])*?)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $ofn = unquote($1);
+ my $fn = unquote($2);
+ print (rename($ofn,$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^CHGRP\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $fn = unquote($2);
+ print (chown(-1,int($1),$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^CHOWN\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $fn = unquote($2);
+ print (chown(int($1),-1,$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^CHMOD\s+([0-7]+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $fn = unquote($2);
+ print (chmod(oct($1),$fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^DELE\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $fn = unquote($1);
+ print (unlink($fn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^RMD\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $dn = unquote($1);
+ print (rmdir($dn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^MKD\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $dn = unquote($1);
+ if (mkdir($dn,0777)) {
+ print "### 200\n";
+ } else {
+ my $err = $!;
+ print (chdir($dn)?"### 501 $err\n":"### 500 $err\n");
+ }
+ next;
+ };
+ /^CWD\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $dn = unquote($1);
+ print (chdir($dn)?"### 200\n":"### 500 $!\n");
+ next;
+ };
+ /^LIST\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ list($1, 1);
+ next;
+ };
+ /^STAT\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ list($1, 0);
+ next;
+ };
+ /^WRITE\s+(\d+)\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ write_loop($2,$3,O_WRONLY|O_CREAT,$1);
+ next;
+ };
+ /^APPEND\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ write_loop($1,$2,O_WRONLY|O_APPEND);
+ next;
+ };
+ /^STOR\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ write_loop($1,$2,O_WRONLY|O_CREAT|O_TRUNC);
+ next;
+ };
+ /^RETR\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ read_loop($1);
+ next;
+ };
+ /^READ\s+(\d+)\s+(\d+)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ read_loop($3,$2,$1);
+ next;
+ };
+ /^EXEC\s+((?:\\.|[^\\])*?)\s+((?:\\.|[^\\])*?)\s*$/ && do {
+ my $tempfile = unquote($2);
+ my $command = unquote($1);
+ $command = $command . ";echo \"###RESULT: \$?\"";
+ print("### 500 $!\n"), next
+ if (!sysopen(FH,$tempfile,O_CREAT|O_EXCL|O_WRONLY,0600));
+ my $pid = fork();
+ print("### 500 $!\n"), next
+ if (!defined $pid);
+ if ($pid == 0) {
+ open(STDOUT,'>>&FH');
+ open(STDERR,'>>&FH');
+ open(STDIN,'</dev/null'); # not sure here, ms windows anyone?
+ exec('/bin/sh','-c',$command);
+ print STDERR "Couldn't exec /bin/sh: $!\n";
+ exit(255);
+ }
+ waitpid($pid,0);
+ close(FH);
+ print "### 200\n";
+ next;
+ };
+}
+exit(0);
+
+sub list {
+ my $dn = unquote($_[0]);
+ my @entries;
+ if (!-e $dn) {
+ print "### 404 File does not exist\n";
+ return;
+ } elsif ($_[1] && -d _) {
+ opendir(DIR,$dn) || do { print "### 500 $!\n"; return; };
+ @entries = readdir(DIR);
+ closedir(DIR);
+ } else {
+ ($dn, @entries) = $dn =~ m{(.*)/(.*)};
+ $dn = '/' if (!length($dn));
+ }
+ print scalar(@entries),"\n### 100\n";
+ my $cwd = getcwd();
+ chdir($dn) || do { print "### 500 $!\n"; return; };
+ foreach (@entries) {
+ my $link = readlink;
+ my ($mode,$uid,$gid,$size,$mtime) = (lstat)[2,4,5,7,9];
+ print filetype($mode,$link,$uid,$gid);
+ print "S$size\n";
+ print strftime("D%Y %m %d %H %M %S\n",localtime($mtime));
+ print ":$_\n";
+ print "L$link\n" if defined $link;
+ print mimetype($_);
+ print "\n";
+ }
+ chdir($cwd);
+ print "### 200\n";
+}
+
+sub read_loop {
+ my $fn = unquote($_[0]);
+ my ($size) = ($_[1]?int($_[1]):(stat($fn))[7]);
+ my $error = '';
+ print "### 501 Is directory\n" and return if -d $fn;
+ sysopen(FH,$fn,O_RDONLY) || ($error = $!);
+ if ($_[2]) {
+ sysseek(FH,int($_[2]),0) || do { close(FH); $error ||= $!; };
+ }
+ print "### 500 $error\n" and return if $error;
+ if (@_ < 2) {
+ print "$size\n";
+ }
+ print "### 100\n";
+ my $buffer = '';
+ my $read = 1;
+ while ($size > 32768 && ($read = sysread(FH,$buffer,32768)) > 0) {
+#print DEBUG "$size left, $read read\n";
+ $size -= $read;
+ print $buffer;
+ }
+ while ($size > 0 && ($read = sysread(FH,$buffer,$size)) > 0) {
+#print DEBUG "$size left, $read read\n";
+ $size -= $read;
+ print $buffer;
+ }
+ while ($size > 0) {
+ print ' ';
+ $size--;
+ }
+ $error ||= $! if $read <= 0;
+ close(FH);
+ if (!$error) {
+ print "### 200\n";
+ } else {
+ print "### 500 $error\n";
+ }
+}
+
+sub write_loop {
+ my $size = int($_[0]);
+ my $fn = unquote($_[1]);
+#print DEBUG "write_loop called $size size, $fn fn, $_[2]\n";
+ my $error = '';
+ sysopen(FH,$fn,$_[2]) || do { print "### 400 $!\n"; return; };
+ eval { flock(FH,2); };
+ if ($_[3]) {
+ sysseek(FH,int($_[3]),0) || do { close(FH);print "### 400 $!\n"; return; };
+ }
+ <STDIN>;
+ print "### 100\n";
+ my $buffer = '';
+ my $read = 1;
+ while ($size > 32768 && ($read = read(STDIN,$buffer,32768)) > 0) {
+#print DEBUG "$size left, $read read\n";
+ $size -= $read;
+ $error ||= $! if (syswrite(FH,$buffer,$read) != $read);
+ }
+ while ($size > 0 && ($read = read(STDIN,$buffer,$size)) > 0) {
+#print DEBUG "$size left, $read read\n";
+ $size -= $read;
+ $error ||= $! if (syswrite(FH,$buffer,$read) != $read);
+ }
+ close(FH);
+ if (!$error) {
+ print "### 200\n";
+ } else {
+ print "### 500 $error\n";
+ }
+}
+
+sub unquote { $_ = shift; s/\\(.)/$1/g; return $_; }
+
+sub filetype {
+ my ($mode,$link,$uid,$gid) = @_;
+ my $result = 'P';
+ while (1) {
+ -f _ && do { $result .= '-'; last; };
+ -d _ && do { $result .= 'd'; last; };
+ defined($link) && do { $result .= 'l'; last; };
+ -c _ && do { $result .= 'c'; last; };
+ -b _ && do { $result .= 'b'; last; };
+ -S _ && do { $result .= 's'; last; };
+ -p _ && do { $result .= 'p'; last; };
+ $result .= '?'; last;
+ }
+ $result .= ($mode & 0400?'r':'-');
+ $result .= ($mode & 0200?'w':'-');
+ $result .= ($mode & 0100?($mode&04000?'s':'x'):($mode&04000?'S':'-'));
+ $result .= ($mode & 0040?'r':'-');
+ $result .= ($mode & 0020?'w':'-');
+ $result .= ($mode & 0010?($mode&02000?'s':'x'):($mode&02000?'S':'-'));
+ $result .= ($mode & 0004?'r':'-');
+ $result .= ($mode & 0002?'w':'-');
+ $result .= ($mode & 0001?($mode&01000?'t':'x'):($mode&01000?'T':'-'));
+
+ $result .= ' ';
+ $result .= (getpwuid($uid)||$uid);
+ $result .= '.';
+ $result .= (getgrgid($gid)||$gid);
+ $result .= "\n";
+ return $result;
+}
+
+sub mimetype {
+ my $fn = shift;
+ return "Minode/directory\n" if -d $fn;
+ pipe(IN,OUT);
+ my $pid = fork();
+ return '' if (!defined $pid);
+ if ($pid) {
+ close(OUT);
+ my $type = <IN>;
+ close(IN);
+ chomp $type;
+ chomp $type;
+ $type =~ s/[,; ].*//;
+ return '' if ($type !~ m/\//);
+ return "M$type\n"
+ }
+ close(IN);
+ sysopen(NULL,'/dev/null',O_RDWR);
+ dup2(fileno(NULL),fileno(STDIN));
+ dup2(fileno(OUT),fileno(STDOUT));
+ dup2(fileno(NULL),fileno(STDERR));
+ exec('/usr/bin/file','-i','-b','-L',$fn);
+ exit(0);
+}
+__END__
diff --git a/kioslave/fish/fish.protocol b/kioslave/fish/fish.protocol
new file mode 100644
index 000000000..c14599d50
--- /dev/null
+++ b/kioslave/fish/fish.protocol
@@ -0,0 +1,81 @@
+[Protocol]
+exec=kio_fish
+protocol=fish
+input=none
+output=filesystem
+listing=Name,Type,Size,Date,Access,Owner,Group,Link,
+reading=true
+writing=true
+makedir=true
+deleting=true
+linking=true
+moving=true
+Icon=remote
+Description=A kioslave for the FISH protocol
+Description[af]='n Kioslave vir die FISH protokol
+Description[be]=Kioslave для пратакола FISH
+Description[bn]=ফিশ (FISH) প্রোটোকল-এর জন্য একটি kioslave
+Description[br]=Ur c'hioslave evit ar c'homenad FISH
+Description[bs]=kioslave za FISH protokol
+Description[ca]=Un kioslave pel protocol FISH
+Description[cs]=Pomocný protokol pro FISH
+Description[csb]=Plugins protokòłu FISH
+Description[da]=En kioslave for FISH-protokollen
+Description[de]=Ein-/Ausgabemodul für das FISH-Protokoll
+Description[el]=Ένας kioslave για το πρωτόκολλο FISH
+Description[eo]=K-enel-sklavo por la FISH protokolo
+Description[es]=Un kioslave para el protocolo FISH
+Description[et]=FISH protokolli IO-moodul
+Description[eu]=FISH protokolorako kioslavea
+Description[fa]=یک kioslave برای قرارداد FISH
+Description[fi]=Liitäntä FISH-yhteyskäytäntö
+Description[fr]=Un module d'entrées / sorties pour le protocole FISH
+Description[fy]=In kioslave foar it FISH protokol
+Description[ga]=kioslave le haghaidh an phrótacail FISH
+Description[gl]=Un kioslave para o protocolo FISH
+Description[he]=ממשק kioslave עבור פרוטוקול FISH
+Description[hi]=फिश प्रोटोकॉल हेतु के-आई-ओ-स्लेव
+Description[hr]=Kioslave za FISH protokol
+Description[hu]=KDE-protokoll a FISH protokollhoz
+Description[is]=kioslave fyrir FISH samskiptaregluna
+Description[it]=Un kioslave per il protocollo FISH
+Description[ja]=FISH プロトコルのための kioslave
+Description[ka]=kioslave FISH პროტოკოლისთვის
+Description[kk]=FISH протоколға арналған файл жүйесінің модулі
+Description[km]=kioslave សម្រាប់​ពិធីការ FISH
+Description[ko]=FISH 프로토콜을 위한 KIO 슬레이브
+Description[lt]=Kiovergas FISH protokolui
+Description[lv]=KIO vergs FISH protokolam
+Description[mk]=КИО-служител за протоколот FISH
+Description[ms]=Hamba kio untuk protokol FISH
+Description[nb]=En IU-slave for FISH-protokollen
+Description[nds]=En In-/Utgaavdeenst för dat FISH-Protokoll
+Description[ne]=FISH प्रोटोकलका लागि एउटा किओस्लेभ
+Description[nl]=Een kioslave voor het protocol FISH
+Description[nn]=Ein IU-slave for FISH-protokollen
+Description[pa]=FISH ਪਰੋਟੋਕਾਲ ਲਈ kioslave
+Description[pl]=Wtyczka protokołu FISH
+Description[pt]=Um 'kioslave' para o protocolo FISH
+Description[pt_BR]=Uma implementação para o protocolo FISH
+Description[ro]=Un dispozitiv de I/E pentru protocolul FISH
+Description[ru]=Модуль файловой системы для протокола FISH
+Description[rw]=kio-umugaragu ya Porotokole FISH
+Description[se]=SO-šláva FISH-protokolla várás
+Description[sk]=kioslave pre protokol FISH
+Description[sl]=kioslave za protokol FISH
+Description[sr]=Kioslave за протокол FISH
+Description[sr@Latn]=Kioslave za protokol FISH
+Description[sv]=En I/O-slav för protokollet FISH
+Description[ta]=FISH நெறிமுறைக்கான ஒரு க்யோஸ்லேவ்
+Description[te]=ఫిష్ ప్రొటొకాల్ కొరకు ఐఒ బానిస
+Description[th]=ตัวนำข้อมูลเข้า-ออกสำหรับโปรโตคอล FISH
+Description[tr]=FISH protokolü için kioslave
+Description[tt]=FISH protokolı öçen birem sistemeneñ modulı
+Description[uk]=Підлеглий B/В для протоколу FISH
+Description[uz]=FISH protokoli uchun KCH-sleyv
+Description[uz@cyrillic]=FISH протоколи учун КЧ-слейв
+Description[vi]=A kioslave (đày tớ vào ra KDE) cho giao thức FISH
+Description[wa]=On kioslave pol protocole FISH
+Description[zh_CN]=FISH 协议的 KIO 仆人
+Description[zh_TW]=用於 FISH 通訊協定的 kioslave
+DocPath=kioslave/fish.html
diff --git a/kioslave/fish/nxfish.protocol b/kioslave/fish/nxfish.protocol
new file mode 100644
index 000000000..f050282af
--- /dev/null
+++ b/kioslave/fish/nxfish.protocol
@@ -0,0 +1,74 @@
+[Protocol]
+exec=kio_fish
+protocol=nxfish
+input=none
+output=filesystem
+listing=Name,Type,Size,Date,Access,Owner,Group,Link,
+reading=true
+writing=true
+makedir=true
+deleting=true
+linking=true
+moving=true
+Icon=remote
+Description=A kioslave for the NXFISH protocol
+Description[af]='n Kioslave vir die NXFISH protokol
+Description[be]=Kioslave для пратакола NXFISH
+Description[br]=Ur c'hioslave evit ar c'homenad NXFISH
+Description[bs]=kioslave za NXFISH protokol
+Description[ca]=Un kioslave pel protocol NXFISH
+Description[cs]=Pomocný protokol pro NXFISH
+Description[csb]=Plugins protokòłu NXFISH
+Description[da]=En kioslave for NXFISH-protokollen
+Description[de]=Ein-/Ausgabemodul für das NXFISH-Protokoll
+Description[el]=Ένας kioslave για το πρωτόκολλο NXFISH
+Description[eo]=K-enel-sklavo por la NXFISH protokolo
+Description[es]=Un kioslave para el protocolo NXFISH
+Description[et]=NXFISH protokolli IO-moodul
+Description[eu]=NXFISH protokolorako kioslavea
+Description[fa]=یک kioslave برای قرارداد NXFISH
+Description[fi]=Liitäntä NXFISH-yhteyskäytäntö
+Description[fr]=Un module d'entrées / sorties pour le protocole NXFISH
+Description[fy]=In kioslave foar it protokol NXFISH
+Description[ga]=kioslave le haghaidh an phrótacail NXFISH
+Description[gl]=Un kioslave para o protocolo NXFISH
+Description[he]=ממשק kioslave עבור פרוטוקול NXFISH
+Description[hr]=Kioslave za NXFISH protokol
+Description[hu]=KDE-protokoll az NXFISH protokollhoz
+Description[is]=kioslave fyrir NXFISH samskiptaregluna
+Description[it]=Un kioslave per il protocollo NXFISH
+Description[ja]=NXFISH プロトコルのための kioslave
+Description[ka]=kioslave NXFISH ოქმისთვის
+Description[kk]=NXFISH протоколы үшін kioslave
+Description[km]=kioslave សម្រាប់​ពិធីការ NXFISH
+Description[ko]=FISH 프로토콜을 위한 KIO 슬레이브
+Description[lt]=Pagalbinė kio programa NXFISH protokolui
+Description[mk]=КИО-служител за протоколот NXFISH
+Description[nb]=En kioskslave for NXFISH-protokollen
+Description[nds]=En In-/Utgaavdeenst för dat NXFISH-Protokoll
+Description[ne]=NXFISH प्रोटोकलका लागि किओस्लेभ
+Description[nl]=Een kioslave voor het protocol NXFISH
+Description[nn]=Ein IU-slave for NXFISH-protokollen
+Description[pa]=NXFISH ਪਰੋਟੋਕਾਲ ਲਈ kioslave
+Description[pl]=Wtyczka protokołu NXFISH
+Description[pt]=Um 'kioslave' para o protocolo NXFISH
+Description[pt_BR]=Uma implementação para o protocolo NXFISH
+Description[ro]=Un kioslave pentru protocolul NXFISH
+Description[ru]=Модуль файловой системы для протокола NXFISH
+Description[se]=SO-šláva NXFISH-protokolla várás
+Description[sk]=kioslave pre protokol NXFISH
+Description[sl]=kioslave za protokol NXFISH
+Description[sr]=Kioslave за протокол NXFISH
+Description[sr@Latn]=Kioslave za protokol NXFISH
+Description[sv]=En I/O-slav för protokollet NXFISH
+Description[te]=ఎన్ ఎక్స్ ఫిష్ ప్రొటొకాల్ కొరకు ఐఒ బానిస
+Description[th]=ตัวนำข้อมูลเข้า-ออกสำหรับโปรโตคอล NXFISH
+Description[tr]=NXFISH protokolü için kioslave
+Description[uk]=Підлеглий B/В для протоколу NXFISH
+Description[uz]=NXFISH protokoli uchun KCH-sleyvi
+Description[uz@cyrillic]=NXFISH протоколи учун КЧ-слейви
+Description[vi]=A kioslave (đày tớ vào ra KDE) cho giao thức NXFISH
+Description[wa]=On kioslave pol protocole NXFISH
+Description[zh_CN]=NXFISH 协议的 KIO 仆人
+Description[zh_TW]=用於 NXFISH 通訊協定的 kioslave
+DocPath=kioslave/fish.html
diff --git a/kioslave/floppy/AUTHORS b/kioslave/floppy/AUTHORS
new file mode 100644
index 000000000..062f9d6cb
--- /dev/null
+++ b/kioslave/floppy/AUTHORS
@@ -0,0 +1,2 @@
+Written and maintained by:
+Alexander Neundorf, neundorf@kde.org
diff --git a/kioslave/floppy/Makefile.am b/kioslave/floppy/Makefile.am
new file mode 100644
index 000000000..5c2533fda
--- /dev/null
+++ b/kioslave/floppy/Makefile.am
@@ -0,0 +1,20 @@
+## Makefile.am of kdebase/kioslave/floppy
+
+INCLUDES= $(all_includes)
+AM_LDFLAGS = $(all_libraries)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_floppy.la
+
+kio_floppy_la_SOURCES = kio_floppy.cpp program.cpp
+kio_floppy_la_LIBADD = $(LIB_KIO)
+kio_floppy_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+kdelnk_DATA = floppy.protocol
+kdelnkdir = $(kde_servicesdir)
+
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_floppy.pot
diff --git a/kioslave/floppy/README b/kioslave/floppy/README
new file mode 100644
index 000000000..99d87bd71
--- /dev/null
+++ b/kioslave/floppy/README
@@ -0,0 +1,7 @@
+this is an ioslave for KDE 2/3 for accessing fat/vfat floppies without
+mounting.
+It is a wrapper around the mtools.
+
+
+Alex
+neundorf@kde.org
diff --git a/kioslave/floppy/TODO b/kioslave/floppy/TODO
new file mode 100644
index 000000000..f4f1e679e
--- /dev/null
+++ b/kioslave/floppy/TODO
@@ -0,0 +1,3 @@
+-error handling (will be done until new year)
+
+Alex
diff --git a/kioslave/floppy/floppy.protocol b/kioslave/floppy/floppy.protocol
new file mode 100644
index 000000000..3dcae5c78
--- /dev/null
+++ b/kioslave/floppy/floppy.protocol
@@ -0,0 +1,14 @@
+[Protocol]
+exec=kio_floppy
+protocol=floppy
+input=none
+output=filesystem
+listing=Name,Type,Size,Date
+reading=true
+writing=true
+makedir=true
+deleting=true
+moving=true
+Icon=3floppy_mount
+DocPath=kioslave/floppy.html
+Class=:local
diff --git a/kioslave/floppy/kio_floppy.cpp b/kioslave/floppy/kio_floppy.cpp
new file mode 100644
index 000000000..ef3d6e6f2
--- /dev/null
+++ b/kioslave/floppy/kio_floppy.cpp
@@ -0,0 +1,1169 @@
+/* This file is part of the KDE project
+
+ Copyright (C) 2000 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <qtextstream.h>
+#include <qcstring.h>
+#include <qfile.h>
+
+#include "kio_floppy.h"
+
+#include <kinstance.h>
+#include <kdebug.h>
+#include <kio/global.h>
+#include <klocale.h>
+
+using namespace KIO;
+
+extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); }
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_floppy" );
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_floppy protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+ kdDebug(7101) << "Floppy: kdemain: starting" << endl;
+
+ FloppyProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+ return 0;
+}
+
+void getDriveAndPath(const QString& path, QString& drive, QString& rest)
+{
+ drive=QString::null;
+ rest=QString::null;
+ QStringList list=QStringList::split("/",path);
+ for (QStringList::Iterator it=list.begin(); it!=list.end(); it++)
+ {
+ if (it==list.begin())
+ drive=(*it)+":";
+ else
+ rest=rest+"/"+(*it);
+ }
+}
+
+FloppyProtocol::FloppyProtocol (const QCString &pool, const QCString &app )
+:SlaveBase( "floppy", pool, app )
+,m_mtool(0)
+,m_stdoutBuffer(0)
+,m_stderrBuffer(0)
+,m_stdoutSize(0)
+,m_stderrSize(0)
+{
+ kdDebug(7101)<<"Floppy::Floppy: -"<<pool<<"-"<<endl;
+}
+
+FloppyProtocol::~FloppyProtocol()
+{
+ delete [] m_stdoutBuffer;
+ delete [] m_stderrBuffer;
+ delete m_mtool;
+ m_mtool=0;
+ m_stdoutBuffer=0;
+ m_stderrBuffer=0;
+}
+
+int FloppyProtocol::readStdout()
+{
+ //kdDebug(7101)<<"Floppy::readStdout"<<endl;
+ if (m_mtool==0) return 0;
+
+ char buffer[16*1024];
+ int length=::read(m_mtool->stdoutFD(),buffer,16*1024);
+ if (length<=0) return 0;
+
+ //+1 gives us room for a terminating 0
+ char *newBuffer=new char[length+m_stdoutSize+1];
+ kdDebug(7101)<<"Floppy::readStdout(): length: "<<length<<" m_tsdoutSize: "<<m_stdoutSize<<" +1="<<length+m_stdoutSize+1<<endl;
+ if (m_stdoutBuffer!=0)
+ {
+ memcpy(newBuffer, m_stdoutBuffer, m_stdoutSize);
+ }
+ memcpy(newBuffer+m_stdoutSize, buffer, length);
+ m_stdoutSize+=length;
+ newBuffer[m_stdoutSize]='\0';
+
+ delete [] m_stdoutBuffer;
+ m_stdoutBuffer=newBuffer;
+ //kdDebug(7101)<<"Floppy::readStdout(): -"<<m_stdoutBuffer<<"-"<<endl;
+
+ //kdDebug(7101)<<"Floppy::readStdout ends"<<endl;
+ return length;
+}
+
+int FloppyProtocol::readStderr()
+{
+ //kdDebug(7101)<<"Floppy::readStderr"<<endl;
+ if (m_mtool==0) return 0;
+
+ /*struct timeval tv;
+ tv.tv_sec=0;
+ tv.tv_usec=1000*300;
+ ::select(0,0,0,0,&tv);*/
+
+ char buffer[16*1024];
+ int length=::read(m_mtool->stderrFD(),buffer,16*1024);
+ kdDebug(7101)<<"Floppy::readStderr(): read "<<length<<" bytes"<<endl;
+ if (length<=0) return 0;
+
+ //+1 gives us room for a terminating 0
+ char *newBuffer=new char[length+m_stderrSize+1];
+ memcpy(newBuffer, m_stderrBuffer, m_stderrSize);
+ memcpy(newBuffer+m_stderrSize, buffer, length);
+ m_stderrSize+=length;
+ newBuffer[m_stderrSize]='\0';
+ delete [] m_stderrBuffer;
+ m_stderrBuffer=newBuffer;
+ kdDebug(7101)<<"Floppy::readStderr(): -"<<m_stderrBuffer<<"-"<<endl;
+
+ return length;
+}
+
+void FloppyProtocol::clearBuffers()
+{
+ kdDebug(7101)<<"Floppy::clearBuffers()"<<endl;
+ m_stdoutSize=0;
+ m_stderrSize=0;
+ delete [] m_stdoutBuffer;
+ m_stdoutBuffer=0;
+ delete [] m_stderrBuffer;
+ m_stderrBuffer=0;
+ //kdDebug(7101)<<"Floppy::clearBuffers() ends"<<endl;
+}
+
+void FloppyProtocol::terminateBuffers()
+{
+ //kdDebug(7101)<<"Floppy::terminateBuffers()"<<endl;
+ //append a terminating 0 to be sure
+ if (m_stdoutBuffer!=0)
+ {
+ m_stdoutBuffer[m_stdoutSize]='\0';
+ }
+ if (m_stderrBuffer!=0)
+ {
+ m_stderrBuffer[m_stderrSize]='\0';
+ }
+ //kdDebug(7101)<<"Floppy::terminateBuffers() ends"<<endl;
+}
+
+bool FloppyProtocol::stopAfterError(const KURL& url, const QString& drive)
+{
+ if (m_stderrSize==0)
+ return true;
+ //m_stderrBuffer[m_stderrSize]='\0';
+
+ QString outputString(m_stderrBuffer);
+ QTextIStream output(&outputString);
+ QString line=output.readLine();
+ kdDebug(7101)<<"line: -"<<line<<"-"<<endl;
+ if (line.find("resource busy") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access drive %1.\nThe drive is still busy.\nWait until it is inactive and then try again.").arg(drive));
+ }
+ else if ((line.find("Disk full") > -1) || (line.find("No free cluster") > -1))
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not write to file %1.\nThe disk in drive %2 is probably full.").arg(url.prettyURL(),drive));
+ }
+ //file not found
+ else if (line.find("not found") > -1)
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL());
+ }
+ //no disk
+ else if (line.find("not configured") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access %1.\nThere is probably no disk in the drive %2").arg(url.prettyURL(),drive));
+ }
+ else if (line.find("No such device") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access %1.\nThere is probably no disk in the drive %2 or you do not have enough permissions to access the drive.").arg(url.prettyURL(),drive));
+ }
+ else if (line.find("not supported") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access %1.\nThe drive %2 is not supported.").arg(url.prettyURL(),drive));
+ }
+ //not supported or no such drive
+ else if (line.find("Permission denied") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access %1.\nMake sure the floppy in drive %2 is a DOS-formatted floppy disk \nand that the permissions of the device file (e.g. /dev/fd0) are set correctly (e.g. rwxrwxrwx).").arg(url.prettyURL(),drive));
+ }
+ else if (line.find("non DOS media") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not access %1.\nThe disk in drive %2 is probably not a DOS-formatted floppy disk.").arg(url.prettyURL(),drive));
+ }
+ else if (line.find("Read-only") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Access denied.\nCould not write to %1.\nThe disk in drive %2 is probably write-protected.").arg(url.prettyURL(),drive));
+ }
+ else if ((outputString.find("already exists") > -1) || (outputString.find("Skipping ") > -1))
+ {
+ error( KIO::ERR_FILE_ALREADY_EXIST,url.prettyURL());
+ //return false;
+ }
+ else if (outputString.find("could not read boot sector") > -1)
+ {
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not read boot sector for %1.\nThere is probably not any disk in drive %2.").arg(url.prettyURL(),drive));
+ //return false;
+ }
+ else
+ {
+ error( KIO::ERR_UNKNOWN, outputString);
+ }
+ return true;
+}
+
+void FloppyProtocol::listDir( const KURL& _url)
+{
+ kdDebug(7101)<<"Floppy::listDir() "<<_url.path()<<endl;
+ KURL url(_url);
+ QString path(url.path());
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ url.setPath("/a/");
+ redirection(url);
+ finished();
+ return;
+ }
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+
+ QStringList args;
+
+ args<<"mdir"<<"-a"<<(drive+floppyPath);
+ if (m_mtool!=0)
+ delete m_mtool;
+ m_mtool=new Program(args);
+
+ clearBuffers();
+
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mdir");
+ return;
+ }
+
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ delete m_mtool;
+ m_mtool=0;
+ //now mdir has finished
+ //let's parse the output
+ terminateBuffers();
+
+ if (errorOccured)
+ return;
+
+ QString outputString(m_stdoutBuffer);
+ QTextIStream output(&outputString);
+ QString line;
+
+ int totalNumber(0);
+ int mode(0);
+ UDSEntry entry;
+
+ while (!output.atEnd())
+ {
+ line=output.readLine();
+ kdDebug(7101)<<"Floppy::listDir(): line: -"<<line<<"- length: "<<line.length()<<endl;
+
+ if (mode==0)
+ {
+ if (line.isEmpty())
+ {
+ kdDebug(7101)<<"Floppy::listDir(): switching to mode 1"<<endl;
+ mode=1;
+ }
+ }
+ else if (mode==1)
+ {
+ if (line[0]==' ')
+ {
+ kdDebug(7101)<<"Floppy::listDir(): ende"<<endl;
+ totalSize(totalNumber);
+ break;
+ }
+ entry.clear();
+ StatInfo info=createStatInfo(line);
+ if (info.isValid)
+ {
+ createUDSEntry(info,entry);
+ //kdDebug(7101)<<"Floppy::listDir(): creating UDSEntry"<<endl;
+ listEntry( entry, false);
+ totalNumber++;
+ }
+ }
+ }
+ listEntry( entry, true ); // ready
+ finished();
+ //kdDebug(7101)<<"Floppy::listDir() ends"<<endl;
+}
+
+void FloppyProtocol::errorMissingMToolsProgram(const QString& name)
+{
+ error(KIO::ERR_SLAVE_DEFINED,i18n("Could not start program \"%1\".\nEnsure that the mtools package is installed correctly on your system.").arg(name));
+ }
+
+void FloppyProtocol::createUDSEntry(const StatInfo& info, UDSEntry& entry)
+{
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = info.name;
+ entry.append( atom );
+
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = info.size;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MODIFICATION_TIME;
+ atom.m_long = info.time;
+ entry.append( atom );
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long=info.mode;
+ entry.append( atom );
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long =(info.isDir?S_IFDIR:S_IFREG);
+ entry.append( atom );
+}
+
+StatInfo FloppyProtocol::createStatInfo(const QString line, bool makeStat, const QString& dirName)
+{
+ //kdDebug(7101)<<"Floppy::createUDSEntry()"<<endl;
+ QString name;
+ QString size;
+ bool isDir(false);
+ QString day,month, year;
+ QString hour, minute;
+ StatInfo info;
+
+ if (line.length()==41)
+ {
+ int nameLength=line.find(' ');
+ kdDebug(7101)<<"Floppy::createStatInfo: line find: "<<nameLength <<"= -"<<line<<"-"<<endl;
+ if (nameLength>0)
+ {
+ name=line.mid(0,nameLength);
+ QString ext=line.mid(9,3);
+ ext=ext.stripWhiteSpace();
+ if (!ext.isEmpty())
+ name+="."+ext;
+ }
+ kdDebug(7101)<<"Floppy::createStatInfo() name 8.3= -"<<name<<"-"<<endl;
+ }
+ else if (line.length()>41)
+ {
+ name=line.mid(42);
+ kdDebug(7101)<<"Floppy::createStatInfo() name vfat: -"<<name<<"-"<<endl;
+ }
+ if ((name==".") || (name==".."))
+ {
+ if (makeStat)
+ name=dirName;
+ else
+ {
+ info.isValid=false;
+ return info;
+ }
+ }
+
+ if (line.mid(13,5)=="<DIR>")
+ {
+ //kdDebug(7101)<<"Floppy::createUDSEntry() isDir"<<endl;
+ size="1024";
+ isDir=true;
+ }
+ else
+ {
+ size=line.mid(13,9);
+ //kdDebug(7101)<<"Floppy::createUDSEntry() size: -"<<size<<"-"<<endl;
+ }
+
+ //TEEKANNE JPG 70796 01-02-2003 17:47 Teekanne.jpg
+ if (line[25]=='-')
+ {
+ month=line.mid(23,2);
+ day=line.mid(26,2);
+ year=line.mid(29,4);
+ }
+ else //SETUP PKG 1019 1997-09-25 10:31 setup.pkg
+ {
+ year=line.mid(23,4);
+ month=line.mid(28,2);
+ day=line.mid(31,2);
+ }
+ hour=line.mid(35,2);
+ minute=line.mid(38,2);
+ //kdDebug(7101)<<"Floppy::createUDSEntry() day: -"<<day<<"-"<<month<<"-"<<year<<"- -"<<hour<<"-"<<minute<<"-"<<endl;
+
+ if (name.isEmpty())
+ {
+ info.isValid=false;
+ return info;
+ }
+
+ info.name=name;
+ info.size=size.toInt();
+
+ QDateTime date(QDate(year.toInt(),month.toInt(),day.toInt()),QTime(hour.toInt(),minute.toInt()));
+ info.time=date.toTime_t();
+
+ if (isDir)
+ info.mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH| S_IWOTH|S_IWGRP|S_IWUSR ;
+ else
+ info.mode = S_IRUSR | S_IRGRP | S_IROTH| S_IWOTH|S_IWGRP|S_IWUSR;
+
+ info.isDir=isDir;
+
+ info.isValid=true;
+ //kdDebug(7101)<<"Floppy::createUDSEntry() ends"<<endl;
+ return info;
+}
+
+StatInfo FloppyProtocol::_stat(const KURL& url)
+{
+ StatInfo info;
+
+ QString path(url.path());
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+
+ if (floppyPath.isEmpty())
+ {
+ kdDebug(7101)<<"Floppy::_stat(): floppyPath.isEmpty()"<<endl;
+ info.name=path;
+ info.size=1024;
+ info.time=0;
+ info.mode=S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH| S_IWOTH|S_IWGRP|S_IWUSR;
+ info.isDir=true;
+ info.isValid=true;
+
+ return info;
+ }
+
+ //kdDebug(7101)<<"Floppy::_stat(): delete m_mtool"<<endl;
+ if (m_mtool!=0)
+ delete m_mtool;
+
+ QStringList args;
+ args<<"mdir"<<"-a"<<(drive+floppyPath);
+
+ //kdDebug(7101)<<"Floppy::_stat(): create m_mtool"<<endl;
+ m_mtool=new Program(args);
+
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mdir");
+ return info;
+ }
+
+
+ clearBuffers();
+
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ //kdDebug(7101)<<"Floppy::_stat(): delete m_mtool"<<endl;
+ delete m_mtool;
+ m_mtool=0;
+ //now mdir has finished
+ //let's parse the output
+ terminateBuffers();
+
+ if (errorOccured)
+ {
+ info.isValid=false;
+ return info;
+ }
+
+ if (m_stdoutSize==0)
+ {
+ info.isValid=false;
+ error( KIO::ERR_COULD_NOT_STAT, url.prettyURL());
+ return info;
+ }
+
+ kdDebug(7101)<<"Floppy::_stat(): parse stuff"<<endl;
+ QString outputString(m_stdoutBuffer);
+ QTextIStream output(&outputString);
+ QString line;
+ for (int lineNumber=0; !output.atEnd(); lineNumber++)
+ {
+ line=output.readLine();
+ if ( (lineNumber<3) || (line.isEmpty()) )
+ continue;
+ StatInfo info=createStatInfo(line,true,url.fileName());
+ if (info.isValid==false)
+ error( KIO::ERR_COULD_NOT_STAT, url.prettyURL());
+ return info;
+ }
+ if (info.isValid==false)
+ error( KIO::ERR_COULD_NOT_STAT, url.prettyURL());
+ return info;
+}
+
+int FloppyProtocol::freeSpace(const KURL& url)
+{
+ QString path(url.path());
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+
+ //kdDebug(7101)<<"Floppy::freeSpace(): delete m_mtool"<<endl;
+ if (m_mtool!=0)
+ delete m_mtool;
+
+ QStringList args;
+ args<<"mdir"<<"-a"<<drive;
+
+ //kdDebug(7101)<<"Floppy::freeSpace(): create m_mtool"<<endl;
+ m_mtool=new Program(args);
+
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mdir");
+ return -1;
+ }
+
+
+ clearBuffers();
+
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ //kdDebug(7101)<<"Floppy::freeSpace(): delete m_mtool"<<endl;
+ delete m_mtool;
+ m_mtool=0;
+ //now mdir has finished
+ //let's parse the output
+ terminateBuffers();
+
+ if (errorOccured)
+ {
+ return -1;
+ }
+
+ if (m_stdoutSize==0)
+ {
+ error( KIO::ERR_COULD_NOT_STAT, url.prettyURL());
+ return -1;
+ }
+
+ kdDebug(7101)<<"Floppy::freeSpace(): parse stuff"<<endl;
+ QString outputString(m_stdoutBuffer);
+ QTextIStream output(&outputString);
+ QString line;
+ int lineNumber(0);
+ while (!output.atEnd())
+ {
+ line=output.readLine();
+ if (line.find("bytes free")==36)
+ {
+ QString tmp=line.mid(24,3);
+ tmp=tmp.stripWhiteSpace();
+ tmp+=line.mid(28,3);
+ tmp=tmp.stripWhiteSpace();
+ tmp+=line.mid(32,3);
+ tmp=tmp.stripWhiteSpace();
+
+ return tmp.toInt();
+ }
+ lineNumber++;
+ }
+ return -1;
+}
+
+void FloppyProtocol::stat( const KURL & _url)
+{
+ kdDebug(7101)<<"Floppy::stat() "<<_url.path()<<endl;
+ KURL url(_url);
+ QString path(url.path());
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ url.setPath("/a/");
+ redirection(url);
+ finished();
+ return;
+ }
+ StatInfo info=this->_stat(url);
+ if (info.isValid)
+ {
+ UDSEntry entry;
+ createUDSEntry(info,entry);
+ statEntry( entry );
+ finished();
+ //kdDebug(7101)<<"Floppy::stat(): ends"<<endl;
+ return;
+ }
+ //otherwise the error() was already reported in _stat()
+}
+
+void FloppyProtocol::mkdir( const KURL& url, int)
+{
+ kdDebug(7101)<<"FloppyProtocol::mkdir()"<<endl;
+ QString path(url.path());
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ KURL newUrl(url);
+ newUrl.setPath("/a/");
+ redirection(newUrl);
+ finished();
+ return;
+ }
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+ if (floppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+ if (m_mtool!=0)
+ delete m_mtool;
+ //kdDebug(7101)<<"Floppy::stat(): create args"<<endl;
+ QStringList args;
+
+ args<<"mmd"<<(drive+floppyPath);
+ kdDebug(7101)<<"Floppy::mkdir(): executing: mmd -"<<(drive+floppyPath)<<"-"<<endl;
+
+ m_mtool=new Program(args);
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mmd");
+ return;
+ }
+
+
+ clearBuffers();
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ delete m_mtool;
+ m_mtool=0;
+ terminateBuffers();
+ if (errorOccured)
+ return;
+ finished();
+}
+
+void FloppyProtocol::del( const KURL& url, bool isfile)
+{
+ kdDebug(7101)<<"FloppyProtocol::del()"<<endl;
+ QString path(url.path());
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ KURL newUrl(url);
+ newUrl.setPath("/a/");
+ redirection(newUrl);
+ finished();
+ return;
+ }
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+ if (floppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+
+ if (m_mtool!=0)
+ delete m_mtool;
+ //kdDebug(7101)<<"Floppy::stat(): create args"<<endl;
+ QStringList args;
+
+ bool usingmdel;
+
+ if (isfile)
+ {
+ args<<"mdel"<<(drive+floppyPath);
+ usingmdel=true;
+ }
+ else
+ {
+ args<<"mrd"<<(drive+floppyPath);
+ usingmdel=false;
+ }
+
+ kdDebug(7101)<<"Floppy::del(): executing: " << (usingmdel ? QString("mdel") : QString("mrd") ) << "-"<<(drive+floppyPath)<<"-"<<endl;
+
+ m_mtool=new Program(args);
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram(usingmdel ? QString("mdel") : QString("mrd"));
+ return;
+ }
+
+
+ clearBuffers();
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ delete m_mtool;
+ m_mtool=0;
+ terminateBuffers();
+ if (errorOccured)
+ return;
+ finished();
+}
+
+void FloppyProtocol::rename( const KURL &src, const KURL &dest, bool _overwrite )
+{
+ QString srcPath(src.path());
+ QString destPath(dest.path());
+
+ kdDebug(7101)<<"Floppy::rename() -"<<srcPath<<"- to -"<<destPath<<"-"<<endl;
+
+ if ((srcPath.isEmpty()) || (srcPath=="/"))
+ srcPath="/a/";
+
+ if ((destPath.isEmpty()) || (destPath=="/"))
+ destPath="/a/";
+
+ QString srcDrive;
+ QString srcFloppyPath;
+ getDriveAndPath(srcPath,srcDrive,srcFloppyPath);
+ if (srcFloppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+
+ QString destDrive;
+ QString destFloppyPath;
+ getDriveAndPath(destPath,destDrive,destFloppyPath);
+ if (destFloppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+
+ if (m_mtool!=0)
+ delete m_mtool;
+ //kdDebug(7101)<<"Floppy::stat(): create args"<<endl;
+ QStringList args;
+
+ if (_overwrite)
+ args<<"mren"<<"-o"<<(srcDrive+srcFloppyPath)<<(destDrive+destFloppyPath);
+ else
+ args<<"mren"<<"-D"<<"s"<<(srcDrive+srcFloppyPath)<<(destDrive+destFloppyPath);
+
+ kdDebug(7101)<<"Floppy::move(): executing: mren -"<<(srcDrive+srcFloppyPath)<<" "<<(destDrive+destFloppyPath)<<endl;
+
+ m_mtool=new Program(args);
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mren");
+ return;
+ }
+
+
+ clearBuffers();
+ int result;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ if (readStdout()==0)
+ loopFinished=true;
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(src,srcDrive))
+ {
+ loopFinished=true;
+ errorOccured=true;
+ }
+ }
+ } while (!loopFinished);
+
+ delete m_mtool;
+ m_mtool=0;
+ terminateBuffers();
+ if (errorOccured)
+ return;
+ finished();
+}
+
+void FloppyProtocol::get( const KURL& url )
+{
+ QString path(url.path());
+ kdDebug(7101)<<"Floppy::get() -"<<path<<"-"<<endl;
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ KURL newUrl(url);
+ newUrl.setPath("/a/");
+ redirection(newUrl);
+ finished();
+ return;
+ }
+ StatInfo info=this->_stat(url);
+ //the error was already reported in _stat()
+ if (info.isValid==false)
+ return;
+
+ totalSize( info.size);
+
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+ if (floppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+
+ if (m_mtool!=0)
+ delete m_mtool;
+ //kdDebug(7101)<<"Floppy::stat(): create args"<<endl;
+ QStringList args;
+ args<<"mcopy"<<(drive+floppyPath)<<"-";
+
+ kdDebug(7101)<<"Floppy::get(): executing: mcopy -"<<(drive+floppyPath)<<"-"<<endl;
+
+ m_mtool=new Program(args);
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mcopy");
+ return;
+ }
+
+ clearBuffers();
+ int result;
+ int bytesRead(0);
+ QByteArray array;
+ bool loopFinished(false);
+ bool errorOccured(false);
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ result=m_mtool->select(1,0,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ {
+ delete [] m_stdoutBuffer;
+ m_stdoutBuffer=0;
+ m_stdoutSize=0;
+ if (readStdout()>0)
+ {
+ kdDebug(7101)<<"Floppy::get(): m_stdoutSize:"<<m_stdoutSize<<endl;
+ bytesRead+=m_stdoutSize;
+ array.setRawData(m_stdoutBuffer, m_stdoutSize);
+ data( array );
+ array.resetRawData(m_stdoutBuffer, m_stdoutSize);
+
+ }
+ else
+ {
+ loopFinished=true;
+ }
+ }
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ loopFinished=true;
+ else
+ if (stopAfterError(url,drive))
+ {
+ errorOccured=true;
+ loopFinished=true;
+ }
+ }
+ } while (!loopFinished);
+
+ //kdDebug(7101)<<"Floppy::get(): deleting m_mtool"<<endl;
+ delete m_mtool;
+ m_mtool=0;
+ if (errorOccured)
+ return;
+
+ //kdDebug(7101)<<"Floppy::get(): finishing"<<endl;
+ data( QByteArray() );
+ finished();
+}
+
+void FloppyProtocol::put( const KURL& url, int , bool overwrite, bool )
+{
+ QString path(url.path());
+ kdDebug(7101)<<"Floppy::put() -"<<path<<"-"<<endl;
+
+ if ((path.isEmpty()) || (path=="/"))
+ {
+ KURL newUrl(url);
+ newUrl.setPath("/a/");
+ redirection(newUrl);
+ finished();
+ return;
+ }
+ QString drive;
+ QString floppyPath;
+ getDriveAndPath(path,drive,floppyPath);
+ if (floppyPath.isEmpty())
+ {
+ finished();
+ return;
+ }
+ int freeSpaceLeft=freeSpace(url);
+ if (freeSpaceLeft==-1)
+ return;
+
+ if (m_mtool!=0)
+ delete m_mtool;
+ //kdDebug(7101)<<"Floppy::stat(): create args"<<endl;
+ QStringList args;
+ if (overwrite)
+ args<<"mcopy"<<"-o"<<"-"<<(drive+floppyPath);
+ else
+ args<<"mcopy"<<"-s"<<"-"<<(drive+floppyPath);
+
+ kdDebug(7101)<<"Floppy::put(): executing: mcopy -"<<(drive+floppyPath)<<"-"<<endl;
+
+ m_mtool=new Program(args);
+ if (!m_mtool->start())
+ {
+ delete m_mtool;
+ m_mtool=0;
+ errorMissingMToolsProgram("mcopy");
+ return;
+ }
+
+
+ clearBuffers();
+ int result(0);
+ int bytesRead(0);
+ QByteArray array;
+
+ //from file.cc
+ // Loop until we got 0 (end of data)
+ do
+ {
+ bool stdoutEvent;
+ bool stderrEvent;
+ kdDebug(7101)<<"Floppy::put(): select()..."<<endl;
+ m_mtool->select(0,100,stdoutEvent, stderrEvent);
+ if (stdoutEvent)
+ {
+ if (readStdout()==0)
+ result=0;
+ }
+ if (stderrEvent)
+ {
+ if (readStderr()==0)
+ result=0;
+ else
+ if (stopAfterError(url,drive))
+ result=-1;
+ kdDebug(7101)<<"Floppy::put(): error: result=="<<result<<endl;
+ }
+ else
+ {
+ QByteArray buffer;
+ dataReq(); // Request for data
+ //kdDebug(7101)<<"Floppy::put(): after dataReq()"<<endl;
+ result = readData( buffer );
+ //kdDebug(7101)<<"Floppy::put(): after readData(), read "<<result<<" bytes"<<endl;
+ if (result > 0)
+ {
+ bytesRead+=result;
+ kdDebug(7101)<<"Floppy::put() bytesRead: "<<bytesRead<<" space: "<<freeSpaceLeft<<endl;
+ if (bytesRead>freeSpaceLeft)
+ {
+ result=0;
+ error( KIO::ERR_SLAVE_DEFINED, i18n("Could not write to file %1.\nThe disk in drive %2 is probably full.").arg(url.prettyURL(),drive));
+ }
+ else
+ {
+ //kdDebug(7101)<<"Floppy::put(): writing..."<<endl;
+ result=::write(m_mtool->stdinFD(),buffer.data(), buffer.size());
+ kdDebug(7101)<<"Floppy::put(): after write(), wrote "<<result<<" bytes"<<endl;
+ }
+ }
+ }
+ }
+ while ( result > 0 );
+
+ if (result<0)
+ {
+ perror("writing to stdin");
+ error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, url.prettyURL());
+ return;
+ }
+
+ delete m_mtool;
+ m_mtool=0;
+
+ finished();
+}
+
diff --git a/kioslave/floppy/kio_floppy.h b/kioslave/floppy/kio_floppy.h
new file mode 100644
index 000000000..bfc003b9f
--- /dev/null
+++ b/kioslave/floppy/kio_floppy.h
@@ -0,0 +1,78 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KIO_FLOPPY_H
+#define KIO_FLOPPY_H
+
+#include <kio/slavebase.h>
+#include <kio/global.h>
+
+#include "program.h"
+
+#include <qstring.h>
+
+struct StatInfo
+{
+ StatInfo():name(""),time(0),size(0),mode(0),freeSpace(0),isDir(false),isValid(false) {;}
+ QString name;
+ time_t time;
+ int size;
+ int mode;
+ int freeSpace;
+ bool isDir:1;
+ bool isValid:1;
+};
+
+
+class FloppyProtocol : public KIO::SlaveBase
+{
+ public:
+ FloppyProtocol (const QCString &pool, const QCString &app );
+ virtual ~FloppyProtocol();
+
+ virtual void listDir( const KURL& url);
+ virtual void stat( const KURL & url);
+ virtual void mkdir( const KURL& url, int);
+ virtual void del( const KURL& url, bool isfile);
+ virtual void rename(const KURL &src, const KURL &dest, bool overwrite);
+ virtual void get( const KURL& url );
+ virtual void put( const KURL& url, int _mode,bool overwrite, bool _resume );
+ //virtual void copy( const KURL& src, const KURL &dest, int, bool overwrite );
+ protected:
+ Program *m_mtool;
+ int readStdout();
+ int readStderr();
+
+ StatInfo createStatInfo(const QString line, bool makeStat=false, const QString& dirName="");
+ void createUDSEntry(const StatInfo& info, KIO::UDSEntry& entry);
+ StatInfo _stat(const KURL& _url);
+ int freeSpace(const KURL& url);
+
+ bool stopAfterError(const KURL& url, const QString& drive);
+ void errorMissingMToolsProgram(const QString& name);
+
+ void clearBuffers();
+ void terminateBuffers();
+ char *m_stdoutBuffer;
+ char *m_stderrBuffer;
+ int m_stdoutSize;
+ int m_stderrSize;
+};
+
+#endif
diff --git a/kioslave/floppy/program.cpp b/kioslave/floppy/program.cpp
new file mode 100644
index 000000000..7cfd4989a
--- /dev/null
+++ b/kioslave/floppy/program.cpp
@@ -0,0 +1,201 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000-2002 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+#include "program.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <kdebug.h>
+
+Program::Program(const QStringList &args)
+:m_pid(0)
+,mArgs(args)
+,mStarted(false)
+{
+}
+
+Program::~Program()
+{
+ if (m_pid!=0)
+ {
+ ::close(mStdin[0]);
+ ::close(mStdout[0]);
+ ::close(mStderr[0]);
+
+ ::close(mStdin[1]);
+ ::close(mStdout[1]);
+ ::close(mStderr[1]);
+
+ int s(0);
+ //::wait(&s);
+ ::waitpid(m_pid,&s,0);
+ this->kill();
+ ::waitpid(m_pid,&s,WNOHANG);
+ };
+}
+
+bool Program::start()
+{
+ if (mStarted) return false;
+ if (pipe(mStdout)==-1) return false;
+ if (pipe(mStdin )==-1) return false;
+ if (pipe(mStderr )==-1) return false;
+
+ int notificationPipe[2];
+ if (pipe(notificationPipe )==-1) return false;
+
+ m_pid=fork();
+
+ if (m_pid>0)
+ {
+ //parent
+ ::close(mStdin[0]);
+ ::close(mStdout[1]);
+ ::close(mStderr[1]);
+ ::close(notificationPipe[1]);
+ mStarted=true;
+ fd_set notifSet;
+ FD_ZERO(&notifSet);
+ FD_SET(notificationPipe[0],&notifSet);
+ struct timeval tv;
+ //wait up to five seconds
+
+ kdDebug(7101)<<"**** waiting for notification"<<endl;
+ //0.2 sec
+ tv.tv_sec=0;
+ tv.tv_usec=1000*200;
+ int result=::select(notificationPipe[0]+1,&notifSet,0,0,&tv);
+/* if (result<1)
+ {
+ kdDebug(7101)<<"**** waiting for notification: failed "<<result<<endl;
+ return false;
+ }
+ else*/
+ if(result==1)
+ {
+ char buf[256];
+ result=::read(notificationPipe[0],buf,256);
+ //if execvp() failed the child sends us "failed"
+ if (result>0)
+ return false;
+ };
+ kdDebug(7101)<<"**** waiting for notification: succeeded"<<result<<endl;
+ return true;
+ }
+ else if (m_pid==-1)
+ {
+ //failed
+ return false;
+ }
+ else if (m_pid==0)
+ {
+ ::close(notificationPipe[0]);
+
+ //child
+ ::close(0); // close the stdios
+ ::close(1);
+ ::close(2);
+
+ dup(mStdin[0]);
+ dup(mStdout[1]);
+ dup(mStderr[1]);
+
+ ::close(mStdin[1]);
+ ::close(mStdout[0]);
+ ::close(mStderr[0]);
+
+ fcntl(mStdin[0], F_SETFD, FD_CLOEXEC);
+ fcntl(mStdout[1], F_SETFD, FD_CLOEXEC);
+ fcntl(mStderr[1], F_SETFD, FD_CLOEXEC);
+
+ char **arglist=(char**)malloc((mArgs.count()+1)*sizeof(char*));
+ int c=0;
+
+ for (QStringList::Iterator it=mArgs.begin(); it!=mArgs.end(); ++it)
+ {
+ arglist[c]=(char*)malloc((*it).length()+1);
+ strcpy(arglist[c], (*it).latin1());
+ c++;
+ }
+ arglist[mArgs.count()]=0;
+ //make parsing easier
+ putenv(strdup("LANG=C"));
+ execvp(arglist[0], arglist);
+ //we only get here if execvp() failed
+ ::write(notificationPipe[1],"failed",strlen("failed"));
+ ::close(notificationPipe[1]);
+ _exit(-1);
+ };
+ return false;
+}
+
+bool Program::isRunning()
+{
+ return mStarted;
+}
+
+int Program::select(int secs, int usecs, bool& stdoutReceived, bool& stderrReceived/*, bool& stdinWaiting*/)
+{
+ stdoutReceived=false;
+ stderrReceived=false;
+
+ struct timeval tv;
+ tv.tv_sec=secs;
+ tv.tv_usec=usecs;
+
+ fd_set readFDs;
+ FD_ZERO(&readFDs);
+ FD_SET(stdoutFD(),&readFDs);
+ FD_SET(stderrFD(),&readFDs);
+
+ int maxFD=stdoutFD();
+ if (stderrFD()>maxFD) maxFD=stderrFD();
+
+ /*fd_set writeFDs;
+ FD_ZERO(&writeFDs);
+ FD_SET(stdinFD(),&writeFDs);
+ if (stdinFD()>maxFD) maxFD=stdinFD();*/
+ maxFD++;
+
+ int result=::select(maxFD,&readFDs,/*&writeFDs*/0,0,&tv);
+ if (result>0)
+ {
+ stdoutReceived=FD_ISSET(stdoutFD(),&readFDs);
+ stderrReceived=FD_ISSET(stderrFD(),&readFDs);
+ //stdinWaiting=(FD_ISSET(stdinFD(),&writeFDs));
+ };
+ return result;
+}
+
+int Program::kill()
+{
+ if (m_pid==0)
+ return -1;
+ return ::kill(m_pid, SIGTERM);
+ //::kill(m_pid, SIGKILL);
+}
+
diff --git a/kioslave/floppy/program.h b/kioslave/floppy/program.h
new file mode 100644
index 000000000..29ea634ad
--- /dev/null
+++ b/kioslave/floppy/program.h
@@ -0,0 +1,53 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000-2002 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef PROGRAM_H
+#define PROGRAM_H
+
+#include <qstringlist.h>
+
+/**
+ * start programs and write to thieir stdin, stderr,
+ * and read from stdout
+ **/
+class Program
+{
+public:
+ Program(const QStringList &args);
+ ~Program();
+ bool start();
+ bool isRunning();
+
+ int stdinFD() {return mStdin[1];}
+ int stdoutFD() {return mStdout[0];}
+ int stderrFD() {return mStderr[0];}
+ int pid() {return m_pid;}
+ int kill();
+ int select(int secs, int usecs, bool& stdoutReceived, bool& stderrReceived/*, bool& stdinWaiting*/);
+protected:
+ int mStdout[2];
+ int mStdin[2];
+ int mStderr[2];
+ int m_pid;
+ QStringList mArgs;
+ bool mStarted;
+};
+
+#endif
+
diff --git a/kioslave/home/Makefile.am b/kioslave/home/Makefile.am
new file mode 100644
index 000000000..be10e5bcc
--- /dev/null
+++ b/kioslave/home/Makefile.am
@@ -0,0 +1,32 @@
+SUBDIRS= . kdedmodule
+# wizard
+
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kio_home.la
+
+kio_home_la_SOURCES = dummy.cpp
+kio_home_la_LIBADD = libkiohome.la $(LIB_KIO)
+kio_home_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+
+dummy.cpp:
+ echo > dummy.cpp
+
+kde_services_DATA = home.protocol
+
+noinst_LTLIBRARIES = libkiohome.la
+libkiohome_la_SOURCES = kio_home.cpp homeimpl.cpp
+
+check_PROGRAMS = testhome
+testhome_SOURCES = testhome.cpp
+testhome_LDADD = libkiohome.la $(LIB_KIO)
+testhome_LDFLAGS = $(all_libraries)
+
+## TODO in unsermake: TESTS = testhome
+check: testhome
+ ./testhome
+
+messages:
+ $(XGETTEXT) `find . -name "*.cc" -o -name "*.cpp" -o -name "*.h"` -o $(podir)/kio_home.pot
+
diff --git a/kioslave/home/dummy.cpp b/kioslave/home/dummy.cpp
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/kioslave/home/dummy.cpp
@@ -0,0 +1 @@
+
diff --git a/kioslave/home/home.protocol b/kioslave/home/home.protocol
new file mode 100644
index 000000000..59f6cba5d
--- /dev/null
+++ b/kioslave/home/home.protocol
@@ -0,0 +1,19 @@
+[Protocol]
+exec=kio_home
+protocol=home
+input=none
+output=filesystem
+listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group,Link
+reading=true
+writing=true
+makedir=true
+deleting=true
+linking=true
+moving=true
+Icon=folder_home
+maxInstances=4
+#TODO DocPath=kioslave/file.html
+Class=:local
+Parent=system:/
+deleteRecursive=true
+fileNameUsedForCopying=Name
diff --git a/kioslave/home/homeimpl.cpp b/kioslave/home/homeimpl.cpp
new file mode 100644
index 000000000..7e86173ba
--- /dev/null
+++ b/kioslave/home/homeimpl.cpp
@@ -0,0 +1,228 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "homeimpl.h"
+
+#include <kdebug.h>
+#include <qapplication.h>
+#include <qeventloop.h>
+
+#include <sys/stat.h>
+
+#define MINIMUM_UID 500
+
+HomeImpl::HomeImpl()
+{
+ KUser user;
+ m_effectiveUid = user.uid();
+}
+
+bool HomeImpl::parseURL(const KURL &url, QString &name, QString &path) const
+{
+ QString url_path = url.path();
+
+ int i = url_path.find('/', 1);
+ if (i > 0)
+ {
+ name = url_path.mid(1, i-1);
+ path = url_path.mid(i+1);
+ }
+ else
+ {
+ name = url_path.mid(1);
+ path = QString::null;
+ }
+
+ return name != QString::null;
+}
+
+bool HomeImpl::realURL(const QString &name, const QString &path, KURL &url)
+{
+ KUser user(name);
+
+ if ( user.isValid() )
+ {
+ KURL res;
+ res.setPath( user.homeDir() );
+ res.addPath(path);
+ url = res;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool HomeImpl::listHomes(QValueList<KIO::UDSEntry> &list)
+{
+ kdDebug() << "HomeImpl::listHomes" << endl;
+
+ KUser current_user;
+ QValueList<KUserGroup> groups = current_user.groups();
+ QValueList<int> uid_list;
+
+ QValueList<KUserGroup>::iterator groups_it = groups.begin();
+ QValueList<KUserGroup>::iterator groups_end = groups.end();
+
+ for(; groups_it!=groups_end; ++groups_it)
+ {
+ QValueList<KUser> users = (*groups_it).users();
+
+ QValueList<KUser>::iterator it = users.begin();
+ QValueList<KUser>::iterator users_end = users.end();
+
+ for(; it!=users_end; ++it)
+ {
+ if ((*it).uid()>=MINIMUM_UID
+ && !uid_list.contains( (*it).uid() ) )
+ {
+ uid_list.append( (*it).uid() );
+ KIO::UDSEntry entry;
+ createHomeEntry(entry, *it);
+ list.append(entry);
+ }
+ }
+ }
+
+ return true;
+}
+
+static void addAtom(KIO::UDSEntry &entry, unsigned int ID, long l,
+ const QString &s = QString::null)
+{
+ KIO::UDSAtom atom;
+ atom.m_uds = ID;
+ atom.m_long = l;
+ atom.m_str = s;
+ entry.append(atom);
+}
+
+
+void HomeImpl::createTopLevelEntry(KIO::UDSEntry &entry) const
+{
+ entry.clear();
+ addAtom(entry, KIO::UDS_NAME, 0, ".");
+ addAtom(entry, KIO::UDS_FILE_TYPE, S_IFDIR);
+ addAtom(entry, KIO::UDS_ACCESS, 0555);
+ addAtom(entry, KIO::UDS_MIME_TYPE, 0, "inode/directory");
+ addAtom(entry, KIO::UDS_ICON_NAME, 0, "kfm_home");
+ addAtom(entry, KIO::UDS_USER, 0, "root");
+ addAtom(entry, KIO::UDS_GROUP, 0, "root");
+}
+
+void HomeImpl::createHomeEntry(KIO::UDSEntry &entry,
+ const KUser &user)
+{
+ kdDebug() << "HomeImpl::createHomeEntry" << endl;
+
+ entry.clear();
+
+ QString full_name = user.loginName();
+
+ if (!user.fullName().isEmpty())
+ {
+ full_name = user.fullName()+" ("+user.loginName()+")";
+ }
+
+ full_name = KIO::encodeFileName( full_name );
+
+ addAtom(entry, KIO::UDS_NAME, 0, full_name);
+ addAtom(entry, KIO::UDS_URL, 0, "home:/"+user.loginName());
+
+ addAtom(entry, KIO::UDS_FILE_TYPE, S_IFDIR);
+ addAtom(entry, KIO::UDS_MIME_TYPE, 0, "inode/directory");
+
+ QString icon_name = "folder_home2";
+
+ if (user.uid()==m_effectiveUid)
+ {
+ icon_name = "folder_home";
+ }
+
+ addAtom(entry, KIO::UDS_ICON_NAME, 0, icon_name);
+
+ KURL url;
+ url.setPath(user.homeDir());
+ entry += extractUrlInfos(url);
+}
+
+bool HomeImpl::statHome(const QString &name, KIO::UDSEntry &entry)
+{
+ kdDebug() << "HomeImpl::statHome: " << name << endl;
+
+ KUser user(name);
+
+ if (user.isValid())
+ {
+ createHomeEntry(entry, user);
+ return true;
+ }
+
+ return false;
+}
+
+void HomeImpl::slotStatResult(KIO::Job *job)
+{
+ if ( job->error() == 0)
+ {
+ KIO::StatJob *stat_job = static_cast<KIO::StatJob *>(job);
+ m_entryBuffer = stat_job->statResult();
+ }
+
+ qApp->eventLoop()->exitLoop();
+}
+
+KIO::UDSEntry HomeImpl::extractUrlInfos(const KURL &url)
+{
+ m_entryBuffer.clear();
+
+ KIO::StatJob *job = KIO::stat(url, false);
+ connect( job, SIGNAL( result(KIO::Job *) ),
+ this, SLOT( slotStatResult(KIO::Job *) ) );
+ qApp->eventLoop()->enterLoop();
+
+ KIO::UDSEntry::iterator it = m_entryBuffer.begin();
+ KIO::UDSEntry::iterator end = m_entryBuffer.end();
+
+ KIO::UDSEntry infos;
+
+ for(; it!=end; ++it)
+ {
+ switch( (*it).m_uds )
+ {
+ case KIO::UDS_ACCESS:
+ case KIO::UDS_USER:
+ case KIO::UDS_GROUP:
+ case KIO::UDS_CREATION_TIME:
+ case KIO::UDS_MODIFICATION_TIME:
+ case KIO::UDS_ACCESS_TIME:
+ infos.append(*it);
+ break;
+ default:
+ break;
+ }
+ }
+
+ addAtom(infos, KIO::UDS_LOCAL_PATH, 0, url.path());
+
+ return infos;
+}
+
+#include "homeimpl.moc"
+
diff --git a/kioslave/home/homeimpl.h b/kioslave/home/homeimpl.h
new file mode 100644
index 000000000..8c4ace279
--- /dev/null
+++ b/kioslave/home/homeimpl.h
@@ -0,0 +1,57 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef HOMEIMPL_H
+#define HOMEIMPL_H
+
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kurl.h>
+#include <kuser.h>
+
+#include <qstring.h>
+
+class HomeImpl : public QObject
+{
+Q_OBJECT
+
+public:
+ HomeImpl();
+ bool parseURL(const KURL &url, QString &name, QString &path) const;
+ bool realURL(const QString &name, const QString &path, KURL &url);
+
+ bool statHome(const QString &name, KIO::UDSEntry &entry);
+ bool listHomes(QValueList<KIO::UDSEntry> &list);
+
+ void createTopLevelEntry(KIO::UDSEntry &entry) const;
+
+private slots:
+ void slotStatResult(KIO::Job *job);
+
+private:
+ void createHomeEntry(KIO::UDSEntry& entry, const KUser &user);
+
+ KIO::UDSEntry extractUrlInfos(const KURL &url);
+ KIO::UDSEntry m_entryBuffer;
+
+
+ long m_effectiveUid;
+};
+
+#endif
diff --git a/kioslave/home/kdedmodule/Makefile.am b/kioslave/home/kdedmodule/Makefile.am
new file mode 100644
index 000000000..3d7a54f9b
--- /dev/null
+++ b/kioslave/home/kdedmodule/Makefile.am
@@ -0,0 +1,13 @@
+kde_module_LTLIBRARIES = kded_homedirnotify.la
+
+METASOURCES = AUTO
+INCLUDES = $(all_includes)
+
+kded_homedirnotify_la_SOURCES = homedirnotify.cpp homedirnotify.skel homedirnotifymodule.cpp homedirnotifymodule.skel
+kded_homedirnotify_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_homedirnotify_la_LIBADD = $(LIB_KSYCOCA)
+
+
+servicesdir = $(kde_servicesdir)/kded
+services_DATA = homedirnotify.desktop
+
diff --git a/kioslave/home/kdedmodule/homedirnotify.cpp b/kioslave/home/kdedmodule/homedirnotify.cpp
new file mode 100644
index 000000000..e4eab44bb
--- /dev/null
+++ b/kioslave/home/kdedmodule/homedirnotify.cpp
@@ -0,0 +1,185 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "homedirnotify.h"
+
+#include <kdebug.h>
+#include <kuser.h>
+
+#include <kdirnotify_stub.h>
+
+#define MINIMUM_UID 500
+
+HomeDirNotify::HomeDirNotify()
+: mInited( false )
+{
+}
+
+void HomeDirNotify::init()
+{
+ if( mInited )
+ return;
+ mInited = true;
+
+ KUser current_user;
+ QValueList<KUserGroup> groups = current_user.groups();
+ QValueList<int> uid_list;
+
+ QValueList<KUserGroup>::iterator groups_it = groups.begin();
+ QValueList<KUserGroup>::iterator groups_end = groups.end();
+
+ for(; groups_it!=groups_end; ++groups_it)
+ {
+ QValueList<KUser> users = (*groups_it).users();
+
+ QValueList<KUser>::iterator it = users.begin();
+ QValueList<KUser>::iterator users_end = users.end();
+
+ for(; it!=users_end; ++it)
+ {
+ if ((*it).uid()>=MINIMUM_UID
+ && !uid_list.contains( (*it).uid() ) )
+ {
+ uid_list.append( (*it).uid() );
+
+ QString name = (*it).loginName();
+ KURL url;
+ url.setPath( (*it).homeDir() );
+
+ m_homeFoldersMap[name] = url;
+ }
+ }
+ }
+}
+
+KURL HomeDirNotify::toHomeURL(const KURL &url)
+{
+ kdDebug() << "HomeDirNotify::toHomeURL(" << url << ")" << endl;
+
+ init();
+ QMap<QString,KURL>::iterator it = m_homeFoldersMap.begin();
+ QMap<QString,KURL>::iterator end = m_homeFoldersMap.end();
+
+ for (; it!=end; ++it)
+ {
+ QString name = it.key();
+ KURL base = it.data();
+
+ if ( base.isParentOf(url) )
+ {
+ QString path = KURL::relativePath(base.path(),
+ url.path());
+ KURL result("home:/"+name+"/"+path);
+ result.cleanPath();
+ kdDebug() << "result => " << result << endl;
+ return result;
+ }
+ }
+
+ kdDebug() << "result => KURL()" << endl;
+ return KURL();
+}
+
+KURL::List HomeDirNotify::toHomeURLList(const KURL::List &list)
+{
+ init();
+ KURL::List new_list;
+
+ KURL::List::const_iterator it = list.begin();
+ KURL::List::const_iterator end = list.end();
+
+ for (; it!=end; ++it)
+ {
+ KURL url = toHomeURL(*it);
+
+ if (url.isValid())
+ {
+ new_list.append(url);
+ }
+ }
+
+ return new_list;
+}
+
+ASYNC HomeDirNotify::FilesAdded(const KURL &directory)
+{
+ kdDebug() << "HomeDirNotify::FilesAdded" << endl;
+
+ KURL new_dir = toHomeURL(directory);
+
+ if (new_dir.isValid())
+ {
+ KDirNotify_stub notifier("*", "*");
+ notifier.FilesAdded( new_dir );
+ }
+}
+
+// This hack is required because of the way we manage .desktop files with
+// Forwarding Slaves, their URL is out of the ioslave (some home:/ files
+// have a file:/ based UDS_URL so that they are executed correctly.
+// Hence, FilesRemoved and FilesChanged does nothing... We're forced to use
+// FilesAdded to re-list the modified directory.
+inline void evil_hack(const KURL::List &list)
+{
+ KDirNotify_stub notifier("*", "*");
+
+ KURL::List notified;
+
+ KURL::List::const_iterator it = list.begin();
+ KURL::List::const_iterator end = list.end();
+
+ for (; it!=end; ++it)
+ {
+ KURL url = (*it).upURL();
+
+ if (!notified.contains(url))
+ {
+ notifier.FilesAdded(url);
+ notified.append(url);
+ }
+ }
+}
+
+
+ASYNC HomeDirNotify::FilesRemoved(const KURL::List &fileList)
+{
+ kdDebug() << "HomeDirNotify::FilesRemoved" << endl;
+
+ KURL::List new_list = toHomeURLList(fileList);
+
+ if (!new_list.isEmpty())
+ {
+ //KDirNotify_stub notifier("*", "*");
+ //notifier.FilesRemoved( new_list );
+ evil_hack(new_list);
+ }
+}
+
+ASYNC HomeDirNotify::FilesChanged(const KURL::List &fileList)
+{
+ kdDebug() << "HomeDirNotify::FilesChanged" << endl;
+
+ KURL::List new_list = toHomeURLList(fileList);
+
+ if (!new_list.isEmpty())
+ {
+ //KDirNotify_stub notifier("*", "*");
+ //notifier.FilesChanged( new_list );
+ evil_hack(new_list);
+ }
+}
diff --git a/kioslave/home/kdedmodule/homedirnotify.desktop b/kioslave/home/kdedmodule/homedirnotify.desktop
new file mode 100644
index 000000000..c931748d1
--- /dev/null
+++ b/kioslave/home/kdedmodule/homedirnotify.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=Service
+Name=KDED Home Base URL Notifier
+Name[af]=KDED tuis URL inkennissteller
+Name[be]=Праверка зменаў мясцовых файлаў KDED
+Name[bs]=KDED lokalno obavještenje o baznom URLu
+Name[ca]=Notificador KDED de l'URL d'inici
+Name[cs]=Démon upozorňování na domovské URL
+Name[csb]=Dôwanié wiédzë o URL-ach domôcegò katalogù dlô KDED
+Name[da]=KDED Hjemmebasis-URL påmindelser
+Name[de]=Überwachung für persönliche Ordner
+Name[el]=KDED ειδοποιητής URL αρχικής βάσης
+Name[eo]=KDED Hejmo Bazo URL Avertilo
+Name[es]=Notificador de URL de KDED
+Name[et]=KDED kodu baas-URLi teadustaja
+Name[eu]=KDED hasierako URL oinarriaren iragarlea
+Name[fa]=اخطاردهندۀ نشانی وب پایۀ آغازۀ KDED
+Name[fi]=KDED:in etä-verkko-osoitteen ilmaisin
+Name[fr]=Notification de l'URL de base KDED
+Name[fy]=KDED Thúsbasis-URL-adres notifikaasje
+Name[gl]=Notificador de URL base de KDED
+Name[hr]=KDED URL obavještavanje domaće baze
+Name[hu]=Értesítő a KDE saját URL-hez
+Name[is]=KDED grunnslóðar tilkynnari
+Name[it]=Notifica KDED Home Base URL
+Name[ja]=KDED ホームベース URL Nofitier
+Name[ka]=KDED ძირითადი თავფურცლის URL შემტყობინებელი
+Name[kk]=KDED Home Base URL құлақтандыру
+Name[km]=KDED Remote Base URL Notifier
+Name[ko]=KDED 원격 기반 URL 알리미
+Name[lt]=KDED pagrindinio namų URL priminiklis
+Name[nb]=KDED-påminner for eksterne nettadresser
+Name[nds]=KDED-Narichten för Tohuusorner-URLs
+Name[ne]=KDED गृह आधारित यूआरएल सूचक
+Name[nl]=KDED Thuisbasis-URL-adres notificatie
+Name[nn]=KDED-varsel for heimebase
+Name[pa]=KDED ਮੁੱਖ ਅਧਾਰ URL ਸੂਚਕ
+Name[pl]=Powiadamianie o URL-ach katalogu domowego dla KDED
+Name[pt]=Notificador de URLs de Base Remotos do KDED
+Name[pt_BR]=Serviço de Notificação da URL do KDED
+Name[ro]=Notificare KDED pentru URL acasă
+Name[ru]=Уведомление о смене базового адреса KDED
+Name[sk]=KDED notifikátor domovskej URL
+Name[sl]=Obvestilnik KDED domačega osnovnega URL-ja
+Name[sr]=Обавештавач о домаћем базном URL-у, KDED
+Name[sr@Latn]=Obaveštavač o domaćem baznom URL-u, KDED
+Name[sv]=KDED-meddelande om hembaswebbadresser
+Name[th]=ตัวแจ้งเตือน Home Base URL KDED
+Name[tr]=KDED Yerel Tabanlı URL Hatırlatıcı
+Name[uk]=Сповіщувач домашньої базової адреси URL KDED
+Name[vi]=Trình thông báo URL trong máy KDED
+Name[wa]=Notifieu di l' URL di båze del måjhon KDED
+Name[zh_CN]=KDED 主页基 URL 通知器
+Name[zh_TW]=KDED 家用基礎 URL 通知程式
+ServiceTypes=KDEDModule
+X-KDE-ModuleType=Library
+X-KDE-Library=homedirnotify
+X-KDE-FactoryName=homedirnotify
+X-KDE-Kded-load-on-demand=true
+X-KDE-Kded-autoload=true
diff --git a/kioslave/home/kdedmodule/homedirnotify.h b/kioslave/home/kdedmodule/homedirnotify.h
new file mode 100644
index 000000000..14655a1cd
--- /dev/null
+++ b/kioslave/home/kdedmodule/homedirnotify.h
@@ -0,0 +1,48 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef HOMEDIRNOTIFY_H
+#define HOMEDIRNOTIFY_H
+
+#include <kurl.h>
+#include <kdirnotify.h>
+
+#include <qmap.h>
+
+class HomeDirNotify : public KDirNotify
+{
+K_DCOP
+
+public:
+ HomeDirNotify();
+
+k_dcop:
+ virtual ASYNC FilesAdded (const KURL &directory);
+ virtual ASYNC FilesRemoved (const KURL::List &fileList);
+ virtual ASYNC FilesChanged (const KURL::List &fileList);
+
+private:
+ void init();
+ KURL toHomeURL(const KURL &url);
+ KURL::List toHomeURLList(const KURL::List &list);
+
+ QMap<QString,KURL> m_homeFoldersMap;
+ bool mInited;
+};
+
+#endif
diff --git a/kioslave/home/kdedmodule/homedirnotifymodule.cpp b/kioslave/home/kdedmodule/homedirnotifymodule.cpp
new file mode 100644
index 000000000..1b91ccc31
--- /dev/null
+++ b/kioslave/home/kdedmodule/homedirnotifymodule.cpp
@@ -0,0 +1,37 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "homedirnotifymodule.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+
+HomeDirNotifyModule::HomeDirNotifyModule(const QCString &obj)
+ : KDEDModule(obj)
+{
+}
+
+extern "C" {
+ KDE_EXPORT KDEDModule *create_homedirnotify(const QCString &obj)
+ {
+ KGlobal::locale()->insertCatalogue("kio_home");
+ return new HomeDirNotifyModule(obj);
+ }
+}
+
diff --git a/kioslave/home/kdedmodule/homedirnotifymodule.h b/kioslave/home/kdedmodule/homedirnotifymodule.h
new file mode 100644
index 000000000..159670fb6
--- /dev/null
+++ b/kioslave/home/kdedmodule/homedirnotifymodule.h
@@ -0,0 +1,36 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef HOMEDIRNOTIFYMODULE_H
+#define HOMEDIRNOTIFYMODULE_H
+
+#include <kdedmodule.h>
+
+#include "homedirnotify.h"
+
+class HomeDirNotifyModule : public KDEDModule
+{
+K_DCOP
+
+public:
+ HomeDirNotifyModule(const QCString &obj);
+private:
+ HomeDirNotify notifier;
+};
+
+#endif
diff --git a/kioslave/home/kio_home.cpp b/kioslave/home/kio_home.cpp
new file mode 100644
index 000000000..36a3161d8
--- /dev/null
+++ b/kioslave/home/kio_home.cpp
@@ -0,0 +1,186 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kcmdlineargs.h>
+#include <kglobal.h>
+
+
+#include "kio_home.h"
+
+static const KCmdLineOptions options[] =
+{
+ { "+protocol", I18N_NOOP( "Protocol name" ), 0 },
+ { "+pool", I18N_NOOP( "Socket name" ), 0 },
+ { "+app", I18N_NOOP( "Socket name" ), 0 },
+ KCmdLineLastOption
+};
+
+extern "C" {
+ int KDE_EXPORT kdemain( int argc, char **argv )
+ {
+ // KApplication is necessary to use other ioslaves
+ putenv(strdup("SESSION_MANAGER="));
+ KCmdLineArgs::init(argc, argv, "kio_home", 0, 0, 0, 0);
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication app( false, false );
+ // We want to be anonymous even if we use DCOP
+ app.dcopClient()->attach();
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ HomeProtocol slave( args->arg(0), args->arg(1), args->arg(2) );
+ slave.dispatchLoop();
+ return 0;
+ }
+}
+
+
+HomeProtocol::HomeProtocol(const QCString &protocol,
+ const QCString &pool, const QCString &app)
+ : ForwardingSlaveBase(protocol, pool, app)
+{
+}
+
+HomeProtocol::~HomeProtocol()
+{
+}
+
+bool HomeProtocol::rewriteURL(const KURL &url, KURL &newUrl)
+{
+ QString name, path;
+
+ if ( !m_impl.parseURL(url, name, path) )
+ {
+ error(KIO::ERR_MALFORMED_URL, url.prettyURL());
+ return false;
+ }
+
+
+ if ( !m_impl.realURL(name, path, newUrl) )
+ {
+ error(KIO::ERR_MALFORMED_URL, url.prettyURL());
+ return false;
+ }
+
+ return true;
+}
+
+
+void HomeProtocol::listDir(const KURL &url)
+{
+ kdDebug() << "HomeProtocol::listDir: " << url << endl;
+
+ if ( url.path().length() <= 1 )
+ {
+ listRoot();
+ return;
+ }
+
+ QString name, path;
+ bool ok = m_impl.parseURL(url, name, path);
+
+ if ( !ok )
+ {
+ error(KIO::ERR_MALFORMED_URL, url.prettyURL());
+ return;
+ }
+
+ ForwardingSlaveBase::listDir(url);
+}
+
+void HomeProtocol::listRoot()
+{
+ KIO::UDSEntry entry;
+
+ KIO::UDSEntryList home_entries;
+ bool ok = m_impl.listHomes(home_entries);
+
+ if (!ok) // can't happen
+ {
+ error(KIO::ERR_UNKNOWN, "");
+ return;
+ }
+
+ totalSize(home_entries.count()+1);
+
+ m_impl.createTopLevelEntry(entry);
+ listEntry(entry, false);
+
+ KIO::UDSEntryListIterator it = home_entries.begin();
+ KIO::UDSEntryListIterator end = home_entries.end();
+
+ for(; it!=end; ++it)
+ {
+ listEntry(*it, false);
+ }
+
+ entry.clear();
+ listEntry(entry, true);
+
+ finished();
+}
+
+void HomeProtocol::stat(const KURL &url)
+{
+ kdDebug() << "HomeProtocol::stat: " << url << endl;
+
+ QString path = url.path();
+ if ( path.isEmpty() || path == "/" )
+ {
+ // The root is "virtual" - it's not a single physical directory
+ KIO::UDSEntry entry;
+ m_impl.createTopLevelEntry( entry );
+ statEntry( entry );
+ finished();
+ return;
+ }
+
+ QString name;
+ bool ok = m_impl.parseURL(url, name, path);
+
+ if ( !ok )
+ {
+ error(KIO::ERR_MALFORMED_URL, url.prettyURL());
+ return;
+ }
+
+ if( path.isEmpty() )
+ {
+ KIO::UDSEntry entry;
+
+ if ( m_impl.statHome(name, entry) )
+ {
+ statEntry(entry);
+ finished();
+ }
+ else
+ {
+ error(KIO::ERR_DOES_NOT_EXIST, url.prettyURL());
+ }
+ }
+ else
+ {
+ ForwardingSlaveBase::stat(url);
+ }
+}
diff --git a/kioslave/home/kio_home.h b/kioslave/home/kio_home.h
new file mode 100644
index 000000000..1d5e237ea
--- /dev/null
+++ b/kioslave/home/kio_home.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KIO_HOME_H
+#define KIO_HOME_H
+
+#include <kio/forwardingslavebase.h>
+#include "homeimpl.h"
+
+class HomeProtocol : public KIO::ForwardingSlaveBase
+{
+public:
+ HomeProtocol(const QCString &protocol, const QCString &pool,
+ const QCString &app);
+ virtual ~HomeProtocol();
+
+ virtual bool rewriteURL(const KURL &url, KURL &newUrl);
+
+ virtual void listDir(const KURL &url);
+ virtual void stat(const KURL &url);
+
+private:
+ void listRoot();
+
+ HomeImpl m_impl;
+};
+
+#endif
diff --git a/kioslave/home/testhome.cpp b/kioslave/home/testhome.cpp
new file mode 100644
index 000000000..e9d64ec68
--- /dev/null
+++ b/kioslave/home/testhome.cpp
@@ -0,0 +1,69 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kio_home.h"
+#include "testhome.h"
+
+#include <config.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+
+#include <stdlib.h>
+
+static bool check(const QString& txt, QString a, QString b)
+{
+ if (a.isEmpty())
+ a = QString::null;
+ if (b.isEmpty())
+ b = QString::null;
+ if (a == b) {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl;
+ }
+ else {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl;
+ exit(1);
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ KApplication::disableAutoDcopRegistration();
+ KCmdLineArgs::init(argc,argv,"testhome", 0, 0, 0, 0);
+ KApplication app;
+
+ TestHome test;
+ test.setup();
+ test.runAll();
+ kdDebug() << "All tests OK." << endl;
+ return 0; // success. The exit(1) in check() is what happens in case of failure.
+}
+
+void TestHome::setup()
+{
+
+}
+
+void TestHome::runAll()
+{
+
+}
+
diff --git a/kioslave/home/testhome.h b/kioslave/home/testhome.h
new file mode 100644
index 000000000..dd8b257e3
--- /dev/null
+++ b/kioslave/home/testhome.h
@@ -0,0 +1,34 @@
+/* This file is part of the KDE project
+ Copyright (c) 2005 Kvin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef TESTHOME_H
+#define TESTHOME_H
+
+class TestHome
+{
+public:
+ TestHome() {}
+ void setup();
+ void runAll();
+
+ // tests
+
+};
+
+#endif
diff --git a/kioslave/info/LICENSE b/kioslave/info/LICENSE
new file mode 100644
index 000000000..9cb70ac77
--- /dev/null
+++ b/kioslave/info/LICENSE
@@ -0,0 +1,22 @@
+The following license is applicable to all files in this directory, with the
+exception of kde-info2html and kde-info2html.conf which are licensed under the GPL,
+since they are based on GPL work.
+
+LICENSE:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/kioslave/info/Makefile.am b/kioslave/info/Makefile.am
new file mode 100644
index 000000000..8db4a20ec
--- /dev/null
+++ b/kioslave/info/Makefile.am
@@ -0,0 +1,21 @@
+## Makefile.am of kdebase/kioslave/info
+
+INCLUDES = $(all_includes)
+
+METASOURCES = AUTO
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_info.la
+
+kio_info_la_SOURCES = info.cc
+kio_info_la_LIBADD = $(LIB_KIO)
+kio_info_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+noinst_HEADERS = info.h
+
+kdelnk_DATA = info.protocol
+kdelnkdir = $(kde_servicesdir)
+
+kio_info_data_DATA = kde-info2html.conf
+kio_info_data_SCRIPTS = kde-info2html
+kio_info_datadir = $(kde_datadir)/kio_info
diff --git a/kioslave/info/info.cc b/kioslave/info/info.cc
new file mode 100644
index 000000000..6b829ec1c
--- /dev/null
+++ b/kioslave/info/info.cc
@@ -0,0 +1,261 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <klocale.h>
+
+#include "info.h"
+
+using namespace KIO;
+
+InfoProtocol::InfoProtocol( const QCString &pool, const QCString &app )
+ : SlaveBase( "info", pool, app )
+ , m_page( "" )
+ , m_node( "" )
+{
+ kdDebug( 7108 ) << "InfoProtocol::InfoProtocol" << endl;
+
+ m_perl = KGlobal::dirs()->findExe( "perl" );
+ m_infoScript = locate( "data", "kio_info/kde-info2html" );
+ m_infoConf = locate("data", "kio_info/kde-info2html.conf");
+
+ if( m_perl.isNull() || m_infoScript.isNull() || m_infoConf.isNull() ) {
+ kdError( 7108 ) << "Critical error: Cannot locate files for HTML-conversion" << endl;
+ QString errorStr;
+ if ( m_perl.isNull() ) {
+ errorStr = "perl.";
+ } else {
+ QString missing =m_infoScript.isNull() ? "kio_info/kde-info2html" : "kio_info/kde-info2html.conf";
+ errorStr = "kde-info2html" + i18n( "\nUnable to locate file %1 which is necessary to run this service. "
+ "Please check your software installation" ).arg( missing );
+ }
+ error( KIO::ERR_CANNOT_LAUNCH_PROCESS, errorStr );
+ exit();
+ }
+
+ kdDebug( 7108 ) << "InfoProtocol::InfoProtocol - done" << endl;
+}
+
+InfoProtocol::~InfoProtocol()
+{
+ kdDebug( 7108 ) << "InfoProtocol::~InfoProtocol" << endl;
+
+ kdDebug( 7108 ) << "InfoProtocol::~InfoProtocol - done" << endl;
+}
+
+void InfoProtocol::get( const KURL& url )
+{
+ kdDebug( 7108 ) << "InfoProtocol::get" << endl;
+ kdDebug( 7108 ) << "URL: " << url.prettyURL() << " , Path :" << url.path() << endl;
+
+ if (url.path()=="/")
+ {
+ KURL newUrl("info:/dir");
+ redirection(newUrl);
+ finished();
+ return;
+ };
+
+ // some people write info://autoconf instead of info:/autoconf
+ if (!url.host().isEmpty()) {
+ KURL newURl(url);
+ newURl.setPath(url.host()+url.path());
+ newURl.setHost(QString::null);
+ redirection(newURl);
+ finished();
+ return;
+ }
+
+ if ( url.path().right(1) == "/" )
+ {
+ // Trailing / are not supported, so we need to remove them.
+ KURL newUrl( url );
+ QString newPath( url.path() );
+ newPath.truncate( newPath.length()-1 );
+ newUrl.setPath( newPath );
+ redirection( newUrl );
+ finished();
+ return;
+ }
+
+ mimeType("text/html");
+ // extract the path and node from url
+ decodeURL( url );
+
+ QString path = KGlobal::iconLoader()->iconPath("up", KIcon::Toolbar, true);
+ int revindex = path.findRev('/');
+ path = path.left(revindex);
+
+ QString cmd = KProcess::quote(m_perl);
+ cmd += " ";
+ cmd += KProcess::quote(m_infoScript);
+ cmd += " ";
+ cmd += KProcess::quote(m_infoConf);
+ cmd += " ";
+ cmd += KProcess::quote(path);
+ cmd += " ";
+ cmd += KProcess::quote(m_page);
+ cmd += " ";
+ cmd += KProcess::quote(m_node);
+
+ kdDebug( 7108 ) << "cmd: " << cmd << endl;
+
+ FILE *file = popen( QFile::encodeName(cmd), "r" );
+ if ( !file ) {
+ kdDebug( 7108 ) << "InfoProtocol::get popen failed" << endl;
+ error( ERR_CANNOT_LAUNCH_PROCESS, cmd );
+ return;
+ }
+
+ char buffer[ 4096 ];
+ QByteArray array;
+
+ bool empty = true;
+ while ( !feof( file ) )
+ {
+ int n = fread( buffer, 1, sizeof( buffer ), file );
+ if ( !n && feof( file ) && empty ) {
+ error( ERR_CANNOT_LAUNCH_PROCESS, cmd );
+ return;
+ }
+ if ( n < 0 )
+ {
+ // ERROR
+ kdDebug( 7108 ) << "InfoProtocol::get ERROR!" << endl;
+ pclose( file );
+ return;
+ }
+
+ empty = false;
+ array.setRawData( buffer, n );
+ data( array );
+ array.resetRawData( buffer, n );
+ }
+
+ pclose( file );
+
+ finished();
+
+ kdDebug( 7108 ) << "InfoProtocol::get - done" << endl;
+}
+
+void InfoProtocol::mimetype( const KURL& /* url */ )
+{
+ kdDebug( 7108 ) << "InfoProtocol::mimetype" << endl;
+
+ // to get rid of those "Open with" dialogs...
+ mimeType( "text/html" );
+
+ // finish action
+ finished();
+
+ kdDebug( 7108 ) << "InfoProtocol::mimetype - done" << endl;
+}
+
+void InfoProtocol::decodeURL( const KURL &url )
+{
+ kdDebug( 7108 ) << "InfoProtocol::decodeURL" << endl;
+
+ /* Notes:
+ *
+ * I cleaned up the URL decoding and chose not to support URLs in the
+ * form "info:/usr/local/share/info/libc.info.gz" or similar which the
+ * older code attempted (and failed, maybe it had worked once) to do.
+ *
+ * The reason is that an obvious use such as viewing a info file off your
+ * infopath would work for the first page, but then all the links would be
+ * wrong. Of course, one could change kde-info2html to make it work, but I don't
+ * think it worthy, others are free to disagree and write the necessary code ;)
+ *
+ * luis pedro
+ */
+
+ if ( url == KURL( "info:/browse_by_file?special=yes" ) ) {
+ m_page = "#special#";
+ m_node = "browse_by_file";
+ kdDebug( 7108 ) << "InfoProtocol::decodeURL - special - browse by file" << endl;
+ return;
+ }
+
+ decodePath( url.path() );
+
+ kdDebug( 7108 ) << "InfoProtocol::decodeURL - done" << endl;
+}
+
+void InfoProtocol::decodePath( QString path )
+{
+ kdDebug( 7108 ) << "InfoProtocol::decodePath(-" <<path<<"-)"<< endl;
+
+ m_page = "dir"; //default
+ m_node = "";
+
+ // remove leading slash
+ if ('/' == path[0]) {
+ path = path.mid( 1 );
+ }
+ //kdDebug( 7108 ) << "Path: " << path << endl;
+
+ int slashPos = path.find( "/" );
+
+ if( slashPos < 0 )
+ {
+ m_page = path;
+ m_node = "Top";
+ return;
+ }
+
+ m_page = path.left( slashPos );
+
+ // remove leading+trailing whitespace
+ m_node = path.right( path.length() - slashPos - 1).stripWhiteSpace ();
+
+ kdDebug( 7108 ) << "InfoProtocol::decodePath - done" << endl;
+}
+
+// A minimalistic stat with only the file type
+// This seems to be enough for konqueror
+void InfoProtocol::stat( const KURL & )
+{
+ UDSEntry uds_entry;
+ UDSAtom uds_atom;
+
+ // Regular file with rwx permission for all
+ uds_atom.m_uds = KIO::UDS_FILE_TYPE;
+ uds_atom.m_long = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ uds_entry.append( uds_atom );
+
+ statEntry( uds_entry );
+
+ finished();
+}
+
+extern "C" { int KDE_EXPORT kdemain( int argc, char **argv ); }
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_info" );
+
+ kdDebug() << "kio_info starting " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_info protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ InfoProtocol slave( argv[2], argv[3] );
+ slave.dispatchLoop();
+
+ return 0;
+}
diff --git a/kioslave/info/info.h b/kioslave/info/info.h
new file mode 100644
index 000000000..ccf41fc5e
--- /dev/null
+++ b/kioslave/info/info.h
@@ -0,0 +1,36 @@
+#ifndef __info_h__
+#define __info_h__
+
+#include <qobject.h>
+
+#include <kio/slavebase.h>
+
+class KProcess;
+
+class InfoProtocol : public KIO::SlaveBase
+{
+public:
+
+ InfoProtocol( const QCString &pool, const QCString &app );
+ virtual ~InfoProtocol();
+
+ virtual void get( const KURL& url );
+ virtual void stat( const KURL& url );
+ virtual void mimetype( const KURL& url );
+
+protected:
+
+ void decodeURL( const KURL &url );
+ void decodePath( QString path );
+
+private:
+
+ QString m_page;
+ QString m_node;
+
+ QString m_perl;
+ QString m_infoScript;
+ QString m_infoConf;
+};
+
+#endif // __info_h__
diff --git a/kioslave/info/info.protocol b/kioslave/info/info.protocol
new file mode 100644
index 000000000..3bb600aea
--- /dev/null
+++ b/kioslave/info/info.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_info
+protocol=info
+input=none
+output=filesystem
+reading=true
+defaultMimetype=text/html
+determineMimetypeFromExtension=false
+DocPath=kioslave/info.html
+Icon=help_index
+Class=:local
diff --git a/kioslave/info/kde-info2html b/kioslave/info/kde-info2html
new file mode 100755
index 000000000..428d65dbb
--- /dev/null
+++ b/kioslave/info/kde-info2html
@@ -0,0 +1,1031 @@
+#!/usr/bin/perl
+#---------------------------------------------------------
+# info2html
+#---------------------------------------------------------
+#
+# PURPOSE
+# This perl script converts info nodes to HTML format.
+# The node is specified on the command line using the
+# syntax
+# (<infofile>)<tag>
+# If <infofile> and/or <tag> are missing, (dir)Top is assumed.
+#
+# AUTHOR
+# Karl Guggisberg <guggis@iam.unibe.ch>
+#
+# Changes for the KDE Help Center (c) 1999 Matthias ELter
+# (me@kde.org)
+#
+# LICENSE
+# GPL
+#
+# HISTORY
+# 11.10.93 V 1.0
+# 14.10.93 V 1.0a some comments added
+# 15.10.93 V 1.0b file for configuration settings
+# 16.10.93 V 1.0c multiple info path possible
+# some bugs in escaping references removed
+# 28.6.94 V 1.0d some minor changes
+# 8.4.95 V 1.1 bug fixes by Tim Witham
+# <twitham@eng.fm.intel.com>
+# March 1999 Changes for use in KDE Help Center
+# February 2000 Changes for bzip2 format
+# Sept. 4 2002 Updated to the KDE look
+# by Hisham Muhammad <hisham@apple2.com>
+# January 30 2003 Ported Hisham's work to HEAD
+# by David Pashley <david@davidpashley.com>
+# March 6 2003 Substitute use of absolute fixed file URLs to images with help:common URLs
+# for the images and style sheet. By Luis Pedro Coelho
+# March 9 2003 Add support for browsing by file. by Luis Pedro Coelho
+# June 11 2003 Update the layout of the sides to the new infopageslayout.
+# by Sven Leiber <s.leiber@web.de>
+#
+#-------------------------------------------------------
+
+use strict;
+
+# set here the full path of the info2html.conf
+push @INC, $1 if $0 =~ m!(.*/)[^/]+$!; # full path of config file is passed in ARGV[1] by caller but let's clean this anyway
+my $IMAGEDIR = "file:$ARGV[1]/"; # TV: broken, broken, not passed
+my $config_file = $ARGV[0];
+delete $ENV{CDPATH};
+delete $ENV{ENV};
+require $config_file; #-- configuration settings
+
+my $STYLESHEET_KDE = "<link rel=\"stylesheet\" href=\"help:common/kde-default.css\" type=\"text/css\"/>";
+my $LOGO_KDE = "<img src=\"help:/common/kde_logo.png\" alt=\"KDE - The K Desktop Environment\" width=\"296\" height=\"79\" border=\"0\">";
+
+# the use of a query should make sure it never conflicts with a "real" path
+my $BROWSE_BY_FILE_PATH = '/browse_by_file?special=yes';
+
+
+my $DONTPRINTYET = 'DONTPRINTYET ';
+
+#-- patterns
+my $NODEBORDER = '\037\014?'; #-- delimiter of an info node
+my $REDIRSEP = '\177'; #-- delimiter in tag tables
+my $WS = '[ \t]+'; #-- white space +
+my $WSS = '[ \t]*'; #-- white space *
+my $TE = '[\t\,\.]'; #-- end of a tag
+my $TAG = '[^\t\,\.]+'; #-- pattern for a tag
+my $FTAG = '[^\)]+'; #-- pattern for a file name in
+ #-- a cross reference
+
+#---------------------------------------------------------
+# DieFileNotFound
+#---------------------------------------------------------
+# Replies and error message if the file '$FileName' is
+# not accessible.
+#---------------------------------------------------------
+sub DieFileNotFound {
+ my ($FileName) = @_;
+ $FileName =~ s/&/&amp;/g;
+ $FileName =~ s/>/&gt;/g;
+ $FileName =~ s/</&lt;/g;
+
+ #-- TEXT : error message if a file could not be opened
+ print <<EOF;
+<head>
+<title>Info: (no page found)</title>
+</head>
+<body>
+<h1>KDE Info Pages Viewer Error</h1>
+ No info page for topic <code>"$FileName"</code> found.<br>
+ You may find what you are looking for at the <a href="man:$FileName">$FileName manpage</a>.
+</body>
+EOF
+ die "\n";
+}
+
+#---------------------------------------------------------
+# Redirect
+#---------------------------------------------------------
+# Since we can't do a kioslave redirection from here, we resort to an HTML
+# redirection.
+#
+# It could be simpler to just output the correct page, but that would leave the
+# the browser URL indication a bit wrong and more importantly we might mess up relative links.
+# Therefore, I implemented it like this which is simpler if not as nice on the end user
+# who sees a flicker.
+#---------------------------------------------------------
+
+sub Redirect {
+ my ($File,$Tag) = @_;
+ print <<EOF;
+ <html><head><title>Doing redirection</title>
+ <meta http-equiv="refresh" content="0; url=info:$File/$Tag">
+ <body>
+ <h1>Redirecting .... </h1>
+ <p>If you are not automatically taken to a new page, <a href="info:$File/$Tag">click here</a> to continue.
+ </body>
+ </html>
+EOF
+
+ exit 0;
+}
+
+#---------------------------------------------------------
+# FileNotFound
+#---------------------------------------------------------
+# If the file is not found and the node is '', try to go through
+# dir entries.
+# This deals with cases like info:ls should open "coreutils/ls invocation"
+#---------------------------------------------------------
+sub FileNotFound {
+ my ($FileName,$NodeName) = @_;
+ DieFileNotFound($FileName) if $NodeName ne 'Top' || $FileName eq 'dir';
+ # Try to find it in dir
+
+ my $DirFileName = &FindFile('dir');
+ if ($DirFileName =~ m/.info.bz2$/ ) {
+ open DIR, "-|", "bzcat", $DirFileName;
+ }
+ elsif ($DirFileName =~ m/.info.gz$/ ) {
+ open DIR, "-|", "gzip", "-dc", $DirFileName;
+ }
+ else {
+ open DIR, $DirFileName;
+ }
+ my $looking = 1;
+ while (<DIR>) {
+ next if $looking && !/\* Menu/;
+ $looking = 0;
+ my @item = &ParseMenuItem($_,'dir');
+ if (!defined(@item)) { next }
+ my ($MenuLinkTag, $MenuLinkFile, $MenuLinkRef, $MenuLinkText) = @item;
+ if ($MenuLinkRef eq $FileName) {
+ &Redirect($MenuLinkFile, $MenuLinkTag);
+ exit 0;
+ }
+ }
+ &DieFileNotFound($FileName);
+}
+
+#---------------------------------------------------------
+# Escape
+#---------------------------------------------------------
+# This procedures escapes some special characeters. The
+# escape sequence follows the WWW guide for escaped
+# characters in URLs
+#---------------------------------------------------------
+sub Escape {
+ my ($Tag) = @_;
+ #-- escaping is not needed anymore KG/28.6.94
+ #-- it is, for "?" %3f (info:/cvs/What is CVS?), kaper/23.7.02
+ $Tag =~ s/ /%20/g; # space
+ $Tag =~ s/\?$/%3f/g; # space
+ $Tag =~ s/\"/%22/g; # space
+ $Tag =~ s/\#/%23/g;
+# $Tag =~ s/\+/%AB/g; # +
+ $Tag;
+}
+
+#----------------------------------------------------------
+# DirnameCheck
+# TV: This is totally broken.
+# I don't know what was the original attempt but that code
+# cannot work ! we cannot match the info name (which has no full path)
+# with the info path ...
+# The only thing i can see (guessed from the || part of the caller)
+# is that we try to reject files with "/" in their name, guessing
+# we pass a man page full path instead of a info file name ...
+# In *that* case, the flow logic is inverted and we should have used "&&"
+# instead of "||"
+#
+# Thus the commented out call...
+#----------------------------------------------------------
+#sub DirnameCheck {
+# my ($Base) = @_;
+# my $Dir = $Base;
+#
+# $Base =~ s!.*/!!g;
+# $Dir =~ s!\Q$Base\E!!;
+#
+# foreach (@info2html::config::INFODIR) {
+# return 1 if $Dir =~ /^$_/;
+# }
+#
+# foreach my $i (split(/:/, $ENV{INFOPATH})) {
+# return 1 if $Dir =~ /^$i/;
+# }
+#
+# return 0;
+#}
+
+#----------------------------------------------------------
+# DeEscape
+#----------------------------------------------------------
+#sub DeEscape {
+# my ($Tag) = @_;
+# #-- deescaping is not needed anymore. KG/28.6.94
+# $Tag =~ s/%AB/+/g;
+# $Tag =~ s/%20/ /g;
+# $Tag =~ s/\.\.\///g;
+# $Tag =~ s/\.\.//g;
+# $Tag =~ s/\.\///g;
+# $Tag;
+#}
+
+sub infocat {
+# Collect them all into an array that can be sorted
+
+ my %InfoFile;
+ my %LinkText;
+ my @dirs;
+
+ foreach my $dir (@info2html::config::INFODIR) {
+ push @dirs, $dir;
+ }
+ if ($ENV{'INFOPATH'}) {
+ foreach my $dir (split(/:/, $ENV{INFOPATH})) {
+ push @dirs, $dir;
+ }
+ }
+
+ foreach my $dir (@dirs) {
+ opendir DIR, $dir;
+ my ($infofile,$filedesc);
+ while ($infofile = readdir(DIR)) {
+ if ($infofile =~ m/.info.bz2$/ ) {
+ open INFOFILE, "-|", "bzcat", "$dir/$infofile";
+ }
+ elsif ($infofile =~ m/.info.gz$/ ) {
+ open INFOFILE, "-|", "gzip", "-dc", "$dir/$infofile";
+ }
+ elsif ($infofile =~ m/.info$/) {
+ open INFOFILE, "-|", "$dir/$infofile";
+ }
+ else {
+ next;
+ }
+ $filedesc = '';
+ my $collect = 0;
+ my $empty = 1;
+ while (<INFOFILE>) {
+ last if (m/END-INFO-DIR-ENTRY/);
+ s/^\* //;
+ chomp;
+ next if /^\s*$/;
+ if ($collect) {
+ $filedesc .= "\n<br>" if ($collect < 16);
+ $filedesc .= $_;
+ --$collect;
+ $empty = 0;
+ } elsif (!$empty && !$collect) {
+ $filedesc .= "<br><b>...</b>\n";
+ last;
+ }
+ $collect=16 if (m/START-INFO-DIR-ENTRY/);
+ }
+ if ($empty) { $filedesc .= 'no description available'; }
+ close INFOFILE;
+ $filedesc .= $infofile if ($filedesc eq "");
+# Add to the hash
+ $LinkText{$filedesc} = "$dir/$infofile";
+ $InfoFile{$filedesc} = "$infofile";
+ }
+ }
+
+# Now output the list
+ my @sorted = sort { lc($a) cmp lc($b) } keys %InfoFile;
+
+ print '<dl>';
+ foreach my $description ( @sorted ) {
+ print <<EOF;
+ <dt> <a href="info:$InfoFile{$description}/Top">$LinkText{$description}</a>
+ <dd>$description
+
+EOF
+ }
+ print '</dl>';
+}
+
+#----------------------------------------------------------
+# ParsHeaderToken
+#----------------------------------------------------------
+# Parses the header line of an info node for a specific
+# link directive (e.g. Up, Prev)
+#
+# Returns a link as (InfoFile,Tag).
+#----------------------------------------------------------
+sub ParsHeaderToken {
+ my ($HeaderLine, $Token) = @_;
+ return ("", "") if $HeaderLine !~ /$Token:/; #-- token not available
+ my ($InfoFile, $node, $Temp);
+ if ($HeaderLine =~ m!$Token:$WS(\(($FTAG)\))!) {
+ $InfoFile = $2;
+ $Temp = $2 ne "" ? '\(' . $2 . '\)' : "";
+ }
+ $node = $1 if $HeaderLine =~ m!$Token:$WS$Temp$WSS([^\t,\n]+)?([\t,\.\n])!;
+ $node ||= "Top";
+ return $InfoFile, $node;
+}
+
+#---------------------------------------------------------
+# ParsHeaderLine
+#--------------------------------------------------------
+# Parses the header line on an info node for all link
+# directives allowed in a header line.
+# Sometimes the keyword 'Previous' is found in stead of
+# 'Prev'. Thats why the redirection line is checked
+# against both of these keywords.
+#-------------------------------------------------------
+sub ParsHeaderLine {
+ my ($HL) = @_;
+ my @LinkList;
+ #-- Node
+ push(@LinkList, &ParsHeaderToken($HL, "Node"));
+ #-- Next
+ push(@LinkList, &ParsHeaderToken($HL, "Next"));
+ #-- Up
+ push(@LinkList, &ParsHeaderToken($HL, "Up"));
+ #-- Prev or Previous
+ my @LinkInfo = &ParsHeaderToken($HL, "Prev");
+ &ParsHeaderToken($HL, "Previous") if $LinkInfo[0] eq "" && $LinkInfo[1] eq "";
+ push(@LinkList, @LinkInfo);
+ return @LinkList;
+}
+
+############################################################
+# turn tabs into correct number of spaces
+#
+sub Tab2Space {
+ my ($line) = @_;
+ $line =~ s/^\t/ /; # 8 leading spaces if initial tab
+ while ($line =~ s/^([^\t]+)(\t)/$1 . ' ' x (8 - length($1) % 8)/e) {
+ } # replace each tab with right num of spaces
+ return $line;
+}
+
+#--------------------------------------------------------
+# ParseMenuItem
+#--------------------------------------------------------
+# Takes a line containing a Menu item and returns a list of
+# ($MenuLinkTag, $MenuLinkFile, $MenuLinkRef, $MenuLinkText)
+# or undef if the parsing fails
+#-------------------------------------------------------
+
+sub ParseMenuItem {
+ my ($Line,$BaseInfoFile) = @_;
+ my ($MenuLinkTag, $MenuLinkFile, $MenuLinkRef, $MenuLinkText);
+ $Line = &Tab2Space($Line); # make sure columns line up well
+
+ if ($Line =~ /\* ([^:]+)::/) { # -- is a simple entry ending with :: ?
+ $MenuLinkTag = $1;
+ $MenuLinkRef = $1;
+ $MenuLinkText = $'; #' --just to help emacs perl-mode
+ $MenuLinkFile = &Escape($BaseInfoFile);
+ } elsif ($Line =~ /\* ([^:]+):(\s*\(($FTAG)\)($TAG)?$TE\.?)?(.*)$/) {
+ $MenuLinkFile = $BaseInfoFile;
+ $MenuLinkRef = $1;
+ $MenuLinkText = $5;
+ if ($2) {
+ $MenuLinkFile = $3;
+ $MenuLinkTag = $4 || 'Top';
+ $MenuLinkText = ($2 ? ' ' x (length($2)+1) : '') . "$5\n";
+ } else {
+ $Line = "$5\n";
+ if ($Line =~ /( *($TAG)?$TE(.*))$/) {
+ $MenuLinkTag = $2;
+ $MenuLinkText = $Line;
+ }
+ }
+ } else {
+ return undef;
+ }
+ $MenuLinkTag = &Escape($MenuLinkTag); # -- escape special chars
+ $MenuLinkText =~ s/^ *//;
+ return ($MenuLinkTag, $MenuLinkFile, $MenuLinkRef, $MenuLinkText);
+}
+
+#--------------------------------------------------------
+# MenuItem2HTML
+#--------------------------------------------------------
+# Transform an info menu item in HTML with references
+#-------------------------------------------------------
+sub MenuItem2HTML {
+ my ($Line, $BaseInfoFile) = @_;
+ my @parse_results = &ParseMenuItem($Line, $BaseInfoFile);
+ if (!defined (@parse_results)) { return $Line; }
+ my ($MenuLinkTag, $MenuLinkFile, $MenuLinkRef, $MenuLinkText) = @parse_results;
+ #-- produce a HTML line
+ return "<tr class=\"infomenutr\"><td class=\"infomenutd\" width=\"30%\"><ul><li><a href=\"info:/$MenuLinkFile/$MenuLinkTag\">$MenuLinkRef</a></ul></td><td class=\"infomenutd\">$MenuLinkText";
+}
+
+#-------------------------------------------------------------
+# ReadIndirectTable
+#------------------------------------------------------------
+# Scans an info file for the occurence of an 'Indirect:'
+# table. Scans the entrys and returns two lists with the
+# filenames and the global offsets.
+#---------------------------------------------------------
+sub ReadIndirectTable {
+ my ($FileName, $FileNames, $Offsets) = @_;
+
+ local *FH1;
+ if ($FileName =~ /\.gz$/) {
+ open FH1, "-|", "gunzip", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } elsif ($FileName =~ /\.bz2$/) {
+ open FH1, "-|", "bunzip2", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } else {
+ open(FH1, $FileName) || &DieFileNotFound($FileName);
+ }
+ #-- scan for start of Indirect: Table
+ local $_;
+ while (<FH1>) {
+ my $Next = <FH1> if /$NODEBORDER/;
+ last if $Next =~ /^Indirect:/i;
+ }
+ #-- scan the entrys and setup the arrays
+ local $_;
+ while (<FH1>) {
+ last if /$NODEBORDER/;
+ if (/([^:]+):[ \t]+(\d+)/) {
+ push(@$FileNames, $1);
+ push(@$Offsets, $2);
+ }
+ }
+ close(FH1);
+}
+
+#---------------------------------------------------------
+# ReadTagTable
+#--------------------------------------------------------
+# Reads in a tag table from an info file.
+# Returns an assoziative array with the tags found.
+# Tags are transformed to lower case (info is not
+# case sensitive for tags).
+# The entrys in the assoziative Array are of the
+# form
+# <file>#<offset>
+# <file> may be empty if an indirect table is
+# present or if the node is located in the
+# main file.
+# 'Exists' indicates if a tag table has been found.
+# 'IsIndirect' indicates if the tag table is based
+# on a indirect table.
+#--------------------------------------------------------
+sub ReadTagTable {
+ my ($FileName, $TagList, $Exists, $IsIndirect) = @_;
+
+ local *FH;
+ if ($FileName =~ /\.gz$/) {
+ open FH, "-|", "gunzip", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } elsif ($FileName =~ /\.bz2$/) {
+ open FH, "-|", "bunzip2", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } else {
+ open FH, $FileName || &DieFileNotFound($FileName);
+ }
+ ($$Exists, $$IsIndirect) = (0, 0);
+ #-- scan for start of tag table
+ local $_;
+ while (<FH>) {
+ if (/$NODEBORDER/) {
+ if (<FH> =~ /^Tag table:/i) {
+ $$Exists = 1;
+ last;
+ }
+ }
+ }
+ #-- scan the entrys
+ local $_;
+ while (<FH>) {
+ $$IsIndirect = 1 if /^\(Indirect\)/i;
+ last if /$NODEBORDER/;
+ if (/Node:[ \t]+([^$REDIRSEP]+)$REDIRSEP(\d+)/) {
+ my ($Tag, $Offset) = (lc($1), $2);
+ my $File = $1 if /File:[ \t]+([^\t,]+)/;
+ $TagList->{$Tag} = $File."#".$Offset;
+ }
+ }
+ close(FH);
+}
+
+#----------------------------------------------------------
+# ParsCrossRefs
+#----------------------------------------------------------
+# scans a line for the existence of cross references and
+# transforms them to HTML using a little icon
+#----------------------------------------------------------
+sub ParsCrossRefs {
+ my ($prev, $Line, $BaseInfoFile) = @_;
+ my ($NewLine, $Token);
+ my ($CrossRef, $CrossRefFile, $CrossRefTag, $CrossRefRef, $CrossRefText);
+ $Line = " " . $Line;
+ if ($prev =~ /\*Note([^\t\,\.]*)$/mi) {
+ $Line = "$prev-NEWLINE-$Line" if $Line =~ /^$TAG$TE/m;
+ }
+ my @Tokens = split(/(\*Note)/i, $Line); # -- split the line
+ while ($Token = shift @Tokens) {
+ $CrossRefTag = $CrossRefRef = $CrossRefFile = $CrossRefText = '';
+ if ($Token !~ /^\*Note/i) { #-- this part is pure text
+ $NewLine .= $Token;
+ next; #-- ... take the next part
+ }
+ $CrossRef = shift(@Tokens);
+ if ($CrossRef !~ /:/) { #-- seems not to be a valid cross ref.
+ $NewLine .= $Token.$CrossRef;
+ next; # -- ... take the next one
+ }
+ if ($CrossRef =~ /^([^:]+)::/) { # -- a simple cross ref..
+ $CrossRefTag = $1;
+ $CrossRefText = $';
+ $CrossRefRef = $CrossRefTag;
+ $CrossRefTag =~ s/-NEWLINE-/ /g;
+ $CrossRefTag =~ s/^\s+//;
+ $CrossRefTag =~ s/\s+/ /g;
+ $CrossRefRef =~ s/-NEWLINE-/\n/g;
+ $CrossRefTag = &Escape($CrossRefTag); # -- escape specials
+ $BaseInfoFile = &Escape($BaseInfoFile);
+ $NewLine .= "<a href=\"info:/$BaseInfoFile/$CrossRefTag\">";
+ $NewLine .= "$CrossRefRef</a>$CrossRefText";
+ next; # -- .. take the next one
+ }
+ if ($CrossRef !~ /$TE/) { # never mind if tag doesn't end on this line
+ $NewLine .= $Token.$CrossRef;
+ next;
+ }
+#print "--- Com. CR : $CrossRef --- \n";
+ if ($CrossRef =~ /([^:]+):/) { #-- A more complicated one ..
+ $CrossRefRef = $1;
+ $CrossRef = $';
+ $CrossRefText = $CrossRef;
+ }
+ if ($CrossRef =~ /^(\s|\n|-NEWLINE-)*\(($FTAG)\)/) { #-- .. with another file ?
+ $CrossRefFile = $2;
+ $CrossRef = $';
+ }
+ $CrossRefTag = $2 if $CrossRef =~ /^(\s|\n|-NEWLINE-)*($TAG)?($TE)/; #-- ... and a tag ?
+ if ($CrossRefTag eq "" && $CrossRefFile eq "") {
+ $NewLine .= "*Note : $CrossRefText$3";
+ next;
+ }
+
+ $CrossRefTag =~ s/-NEWLINE-/ /g;
+ $CrossRefTag =~ s/^\s+//;
+ $CrossRefTag =~ s/\s+/ /g;
+ $CrossRefRef =~ s/-NEWLINE-/\n/g;
+ $CrossRefText =~ s/-NEWLINE-/\n/g;
+ $CrossRefFile = $BaseInfoFile if $CrossRefFile eq "";
+ $CrossRefTag = "Top" if $CrossRefTag eq "";
+ $CrossRefRef = "($CrossRefFile)$CrossRefTag" if $CrossRefRef eq '';
+ $CrossRefTag = &Escape($CrossRefTag); #-- escape specials
+ $CrossRefFile = &Escape($CrossRefFile);
+ #-- append the HTML text
+ $NewLine .= "<a href=\"info:/$CrossRefFile/$CrossRefTag\">";
+ $NewLine .= "$CrossRefRef</a>$CrossRefText";
+ }
+ if ($NewLine =~ /\*Note([^\t\,\.]*)$/i) {
+ return "$DONTPRINTYET$NewLine";
+ } else {
+ $NewLine; #-- return the new line
+ }
+}
+
+
+#-------------------------------------------------------------
+# PrintLinkInfo
+#-------------------------------------------------------------
+# prints the HTML text for a link information in the
+# header of an info node. Uses some icons URLs of icons
+# are specified in 'info2html.conf'.
+#------------------------------------------------------------
+sub PrintLinkInfo {
+ my ($LinkType, $LinkFile, $LinkTag, $BaseInfoFile) = @_;
+ my ($LinkFileEsc, $LinkTypeText);
+ return if $LinkFile eq "" && $LinkTag eq "";
+
+ #-- If no auxiliary file specified use the current info file
+ $LinkFile ||= $BaseInfoFile;
+ my $LinkRef = $LinkTag;
+ $LinkTag = &Escape($LinkTag);
+ $LinkFileEsc = &Escape($LinkFile);
+ #-- print the HTML Text
+ print <<EOF;
+<a href="info:/$LinkFileEsc/$LinkTag">
+ $LinkTypeText
+ <strong>$LinkRef</strong>
+</a>
+EOF
+}
+
+#-------------------------------------------------------------
+# PrintHeader
+#-------------------------------------------------------------
+# Prints the header for an info node in HTML format
+#------------------------------------------------------------
+sub PrintHeader {
+ my ($LinkList, $BaseInfoFile) = @_;
+ my @LinkList = @{$LinkList};
+
+ my $UpcaseInfoFile = $BaseInfoFile;
+ $UpcaseInfoFile =~ tr/a-z/A-Z/;
+ #-- TEXT for the header of an info node
+ print <<EOF;
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html>
+ <head>
+ <title>Info: ($BaseInfoFile) $LinkList[1]</title>
+ $STYLESHEET_KDE
+ </head>
+ <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
+<!--header start-->
+<div style="background-image: url(help:/common/top-middle.png); width: 100%; height: 131px;">
+<div style="position: absolute; right: 0px;">
+<img src="help:/common/top-right-konqueror.png" style="margin: 0px" alt="" />
+</div>
+<div style="position: absolute; left: 0px;">
+<img src="help:/common/top-left.png" style="margin: 0px" alt="" />
+</div>
+<div style="position: absolute; top: 25px; right: 100px; text-align: right; font-size: xx-large; font-weight: bold; text-shadow: #fff 0px 0px 5px; color: #444">
+$UpcaseInfoFile: $LinkList[1]</div>
+</div>
+<div class="header" style="border: none">
+EOF
+ common_headers($LinkList, $BaseInfoFile);
+print <<EOF;
+</div>
+ <div id="contents">
+ <div class="chapter">
+EOF
+}
+
+sub common_headers {
+ my ($LinkList, $BaseInfoFile) = @_;
+ my @LinkList = @{$LinkList};
+ print <<EOF;
+ <tr><td width="33%" align="left" valign="top" class="navLeft">
+EOF
+ &PrintLinkInfo("Prev", $LinkList[6], $LinkList[7], $BaseInfoFile);
+ print <<EOF;
+ </td><td width="34%" align="center" valign="top" class="navCenter">
+EOF
+ &PrintLinkInfo("Up", $LinkList[4], $LinkList[5], $BaseInfoFile);
+ print <<EOF;
+ </td><td width="33%" align="right" valign="top" class="navRight">
+EOF
+ &PrintLinkInfo("Next", $LinkList[2], $LinkList[3], $BaseInfoFile);
+}
+
+#---------------------------------------------------------
+# PrintFooter
+#---------------------------------------------------------
+# prints the footer for an info node in HTML format
+#---------------------------------------------------------
+sub PrintFooter {
+ my ($LinkList, $BaseInfoFile, $LinkFile) = @_;
+
+ $LinkFile ||= $BaseInfoFile;
+
+ #-- TEXT for the footer of an info node
+ print <<EOF;
+ </div>
+ <em>Automatically generated by a version of
+ <a href="$info2html::config::DOC_URL">
+ <b>info2html</b>
+ </a> modified for <a href="http://www.kde.org/">KDE</a></em>.
+ <div class="bottom-nav">
+EOF
+ common_headers($LinkList, $BaseInfoFile);
+ print <<EOF;
+ <!--<br />--><em>$LinkFile</em>
+ </div>
+ </body>
+</html>
+EOF
+}
+
+#----------------------------------------------------------
+# ReplyNotFoundMessage
+#----------------------------------------------------------
+sub ReplyNotFoundMessage {
+ my ($FileName, $Tag) = @_;
+ print <<EOF;
+<head>
+<title>Info Files - Error Message</title>
+</head>
+<h1>Error</h1>
+<body>
+The Info node <em>$Tag</em> in Info file <em>$FileName</em>
+does not exist.
+</body>
+EOF
+}
+
+sub PrintByFileLink {
+ print <<EOF
+
+ <hr width="80%"/>
+ <p>If you did not find what you were looking for try <a href="info:$BROWSE_BY_FILE_PATH">browsing by file</a> to
+ see files from packages which did not update the directory.
+EOF
+}
+
+#-----------------------------------------------------------
+# BrowseByFile
+#-----------------------------------------------------------
+# Shows a list of available files in the system with a short
+# description of them.
+#------------------------------------------------------------
+
+sub BrowseByFile {
+ my @LinkList = ('', '', '', '',
+ 'dir', 'Top', '','',''); # set LinkList[4] & LinkList[5], of course ;)
+ my $BaseInfoFile = 'Available Files';
+ &PrintHeader(\@LinkList, $BaseInfoFile);
+ print <<EOF;
+<h2>Available Files</h2>
+EOF
+ &infocat;
+ &PrintFooter(\@LinkList, $BaseInfoFile);
+}
+
+#-----------------------------------------------------------
+# InfoNode2HTML
+#-----------------------------------------------------------
+# scans an info file for the node with the name '$Tag'
+# starting at the postion '$Offset'.
+# If found the node is tranlated to HTML and printed.
+#------------------------------------------------------------
+sub InfoNode2HTML {
+ my ($FileName, $Offset, $Tag, $BaseInfoFile) = @_;
+
+ local *FH2;
+ if ($FileName =~ /\.gz$/) {
+ open FH2, "-|", "gunzip", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } elsif ($FileName =~ /\.bz2$/) {
+ open FH2, "-|", "bunzip2", "-q", "-d", "-c", $FileName || &DieFileNotFound($FileName);
+ } else {
+ open FH2, $FileName || &DieFileNotFound($FileName);
+ }
+ seek(FH2, $Offset, 0);
+ $Tag =~ tr/A-Z/a-z/; # -- to lowercase
+ #-- scan for the node start
+ my ($Found, @LinkList);
+ local $_;
+ while (<FH2>) {
+ if (/$NODEBORDER/) {
+ my $line = <FH2>;
+ @LinkList = &ParsHeaderLine($line);
+ my $CompareTag = $Tag;
+ $CompareTag =~ s/([^0-9A-Za-z])/\\$1/g; #-- escape special chars !
+ my $Temp = $LinkList[1];
+ $Temp =~ tr/A-Z/a-z/; #-- to lower case
+ if ($Temp =~ /^\s*$CompareTag\s*$/) { #-- node start found ?
+ $Found = 1;
+ last;
+ }
+ }
+ }
+
+ return &ReplyNotFoundMessage($FileName, $Tag) unless $Found; # -- break if not found;
+
+ &PrintHeader(\@LinkList, $BaseInfoFile);
+ my $InMenu = 0;
+ my $prev;
+ my $LineCount = 0;
+ my $Entries = 0;
+ my $Par = 0;
+ my @ParLines = ();
+ my $ParLine=0;
+ my $MayBeText=0;
+ my $MayBeTitle=0;
+ my $Line;
+ my $PrevMenu;
+ local $_;
+ while (<FH2>) {
+ $LineCount++;
+ last if /$NODEBORDER/;
+ #-- replace meta characters
+ #s/"`"([^"'"]*)"'"/"<span class=\"option\">"$1"</span>"/g;
+ s/&/&amp;/g;
+ s/>/&gt;/g;
+ s/</&lt;/g;
+
+ my $Length = length($_);
+ if ($LineCount == 3 && $InMenu == 0 && length($_) == $Length && $Length > 1){ #-- an underline ?
+ if (/^\**$/) {
+ print "<h2>$prev</h2>\n";
+ $prev = "";
+ next;
+ }
+ elsif (/^=*$/) {
+ print "<h3>$prev</h3>\n";
+ $prev = "";
+ next;
+ }
+ else {
+ print "<h4>$prev</h4>\n";
+ $prev = "";
+ next;
+ }
+ }
+
+ if (/^\* Menu/ && $InMenu == 0) { # -- start of menu section ?
+ $InMenu = 1;
+ print "<h3>Menu</h3>\n";
+ }
+ elsif ($InMenu == 1) {
+ # This is pretty crappy code.
+ # A lot of logic (like the ParsCrossRefs and tranforming Variable: etc) is repeated below.
+ # There have been a few bugs which were fixed in one branch of the code and left in the other.
+ # This should be refactored.
+ # LPC (16 March 2003)
+ if (/^\* /) { #-- a menu entry ?
+ if ($Entries == 0) {
+ $Entries = 1;
+ print "<table class=\"infomenutable\">";
+ }
+ print &MenuItem2HTML($_,$BaseInfoFile);
+ }
+ elsif (/^$/) { #-- empty line
+ if ($Entries == 1) {
+ print "</td></tr></table>";
+ $Entries = 0;
+ }
+ print "<br>";
+ }
+ else {
+ $Line = &ParsCrossRefs($prev,$_,$BaseInfoFile);
+ if ($Line =~ /^$DONTPRINTYET/) {
+ $prev = $Line;
+ $prev =~ s/^$DONTPRINTYET//;
+ chomp $prev;
+ }
+ elsif ($LineCount == 2) {
+ $prev = $Line;
+ } else {
+ $prev = $Line;
+ $Line =~ s#- (Variable|Function|Macro|Command|Special Form|User Option|Data Type):.*$#<em><strong>$&</strong></em>#;
+ $Line =~ s/^[ \t]*//;
+ print $Line;
+ }
+ }
+ }
+ else {
+ if (/^ *$/) {
+ if ($MayBeText == 1) {
+ print "<p>$Par</p>"
+ } else {
+ print "<pre>";
+ foreach (@ParLines) {
+ print $_;
+ }
+ print "\n";
+ print "</pre>";
+ }
+ @ParLines = ();
+ $ParLine = 1;
+ $MayBeText = 1;
+ $MayBeTitle = 1;
+ $Par = "";
+ } else {
+ if ($ParLine == 1) {
+ if (!/^ {1,4}[^ ]/ || /[^ ] [^ ]/) {
+ $MayBeText = 0;
+ }
+ } else {
+ if (!/^ ?[^ ]/ || /[^ ] [^ ]/) {
+ $MayBeText = 0;
+ }
+ }
+ $Line = &ParsCrossRefs($prev,$_,$BaseInfoFile);
+ if ($Line =~ /^$DONTPRINTYET/) {
+ $prev = $Line;
+ $prev =~ s/^$DONTPRINTYET//;
+ chomp $prev;
+ } elsif ($LineCount == 2) {
+ $prev = $Line;
+ } else {
+ $prev = $Line;
+ $Line =~ s#- (Variable|Function|Macro|Command|Special Form|User Option):.*$#<strong>$&</strong>#;
+ $Line =~ s/`([^']*)'/`<span class="option">$1<\/span>'/g; #'
+ $Line =~ s/((news|ftp|http):\/\/[A-Za-z0-9\.\/\#\-_\~]*)/<a href="$1">$1<\/a>/g;
+ $Line =~ s/([A-Za-z0-9\.\/\#\-_\~]*\@[A-Za-z0-9\.\/\#\-_\~]*\.[A-Za-z]{2,3})/<a href="mailto:$1">$1<\/a>/g;
+ $Par = $Par . $Line;
+ $ParLines[$ParLine] = $Line;
+ $ParLine++;
+ }
+ }
+ }
+ }
+ if ($Entries == 1) {
+ print "</table>"
+ }
+ if ($PrevMenu =~ "") {
+ print &MenuItem2HTML($PrevMenu,$BaseInfoFile);
+ }
+
+ close(FH2);
+
+ if ($BaseInfoFile =~ m/dir/i
+ && $Tag =~ m/Top/i) {
+ &PrintByFileLink;
+ }
+
+ &PrintFooter(\@LinkList, $BaseInfoFile);
+}
+
+#-------------------------------------------------------------
+# max
+#------------------------------------------------------------
+sub max {
+ my ($a, $b) = @_;
+ return $a >= $b ? $a : $b;
+}
+
+#-----------------------------------------------------------
+# GetFileAndOffset
+#------------------------------------------------------------
+# This procedure locates a specific node in a info file
+# The location is based on the tag and indirect table in
+# basic info file if such tables are available.
+# Because the offsets specified in the tag and in the
+# indirect table are more or less inacurate the computet
+# offset is set back 100 bytes. From this position
+# the specified node will looked for sequentially
+#------------------------------------------------------------
+sub GetFileAndOffset {
+ my ($BaseInfoFile, $NodeName) = @_;
+ my ($Exists, $IsIndirect, $File, $Offset, $FileOffset, %TagList, @FileNames, @Offsets);
+ $NodeName =~ tr/A-Z/a-z/;
+ &ReadIndirectTable($BaseInfoFile, \@FileNames, \@Offsets);
+
+
+# This looks wastefull:
+# We build a whole TagList hash and then use it to lookup the tag info.
+# Why not pass $NodeName to ReadTagTable and let it return just the desired info?
+# lpc (16 March 2003)
+ &ReadTagTable($BaseInfoFile, \%TagList, \$Exists, \$IsIndirect);
+ return "", 0 unless $Exists; #-- no tag table available
+ return "", 0 unless defined $TagList{$NodeName}; #-- tag is not in the tag table
+ ($File, $Offset) = split(/#/, $TagList{$NodeName});
+ return $File, &max($Offset - 100, 0) if $File; #-- there is an explicite
+ #-- not in the tag table
+
+ if ($IsIndirect == 1) {
+ foreach my $i (0..$#Offsets) {
+ $FileOffset = $Offsets[$i] if $Offsets[$i] <= $Offset;
+ $File = $FileNames[$i] if $Offsets[$i] <= $Offset;
+ }
+ return $File, &max($Offset - $FileOffset - 100,0); #-- be safe (-100!)
+ } else {
+ return "", &max($Offset - 100, 0);
+ }
+}
+
+# FindFile: find the given file on the infopath, return full name or "".
+# Let filenames optionally have .info suffix. Try named version first.
+# Handle gzipped file too.
+sub FindFile {
+ my ($File) = @_;
+ return "" if ($File =~ /\.\./);
+ my $Alt = $File =~ /^(.+)\.info$/ ? $1 : $File . '.info';
+ foreach my $Name ($File, $Alt) {
+ my $gzName = $Name . '.gz';
+ my $bz2Name = $Name . '.bz2';
+
+ foreach (@info2html::config::INFODIR) {
+ return "$_/$Name" if -e "$_/$Name";
+ return "$_/$gzName" if -e "$_/$gzName";
+ return "$_/$bz2Name" if -e "$_/$bz2Name";
+ }
+ next unless $ENV{INFOPATH};
+ foreach my $i (split(/:/, $ENV{INFOPATH})) {
+ return "$i/$Name" if -e "$i/$Name";
+ return "$i/$gzName" if -e "$i/$gzName";
+ return "$i/$bz2Name" if -e "$i/$bz2Name";
+ }
+ }
+ return "";
+}
+
+#-------------------------------------------------------
+#
+#------------------- MAIN -----------------------------
+#
+# called as
+# perl /path/kde-info2html config_file image_base_path BaseInfoFile NodeName
+#
+# BaseInfoFile eq '#special#' to pass special args through NodeName (yes, it is a hack).
+#
+
+my $PROGRAM = $0; # determine our basename and version
+$PROGRAM =~ s!.*/!!;
+my ($BaseInfoFile, $NodeName) = ($ARGV[2], $ARGV[3]);
+#&DirnameCheck($BaseInfoFile) || &DieFileNotFound($BaseInfoFile);
+
+if ($BaseInfoFile eq '#special#' && $NodeName eq 'browse_by_file') {
+ &BrowseByFile;
+ exit 0;
+}
+
+$BaseInfoFile = "dir" if $BaseInfoFile =~ /^dir$/i;
+my $FileNameFull = &FindFile($BaseInfoFile) || &FileNotFound($BaseInfoFile,$NodeName);
+my ($File, $Offset) = &GetFileAndOffset($FileNameFull, $NodeName);
+$File ||= $BaseInfoFile;
+$FileNameFull = &FindFile($File);
+&InfoNode2HTML($FileNameFull, $Offset, $NodeName, $BaseInfoFile);
+
+exit 0;
diff --git a/kioslave/info/kde-info2html.conf b/kioslave/info/kde-info2html.conf
new file mode 100644
index 000000000..260d2b336
--- /dev/null
+++ b/kioslave/info/kde-info2html.conf
@@ -0,0 +1,43 @@
+# -*- perl -*-
+package info2html::config;
+#-----------------------------------------------------------------
+# info2html.conf
+#-----------------------------------------------------------------
+# PURPOSE
+# configuration settings for the 'info2html' script.
+#
+# AUTHOR
+# Karl Guggisberg <guggis@iam.unibe.ch>
+#
+# HISTORY
+# 15.10.93 V 1.0b
+# 16.10.93 V 1.0c multple info files possible
+# 28.6.94 V 1.0d some minor changes
+# 8.4.95 V 1.1 some changements
+#----------------------------------------------------------------
+
+use strict;
+#use vars qw(@ISA @EXPORT);
+#
+#@ISA = qw(Exporter);
+#@EXPORT = qw(@INFODIR $DOC_URL);
+
+
+#-- location of info files.
+our @INFODIR = (
+ "/usr/share/info",
+ "/usr/info",
+ "/usr/lib/info",
+# "/usr/lib/teTeX/info",
+ "/usr/local/info",
+ "/usr/local/lib/info",
+ "/usr/X11R6/info",
+ "/usr/X11R6/lib/info",
+ "/usr/X11R6/lib/xemacs/info"
+ );
+
+
+#-- URL for documentation of info2html
+our $DOC_URL = 'http://info2html.sourceforge.net/';
+
+1;
diff --git a/kioslave/ldap/LICENSE b/kioslave/ldap/LICENSE
new file mode 100644
index 000000000..d28a48f92
--- /dev/null
+++ b/kioslave/ldap/LICENSE
@@ -0,0 +1,16 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/kioslave/ldap/Makefile.am b/kioslave/ldap/Makefile.am
new file mode 100644
index 000000000..49faa1e98
--- /dev/null
+++ b/kioslave/ldap/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am of kdebase/kioslave/ldap
+
+INCLUDES = $(all_includes) $(LDAP_INCS)
+AM_CXXFLAGS = -DLDAP_DEPRECATED
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LDAP_RPATH)
+LDADD = $(LIB_KIO) $(LDAP_LIBS)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_ldap.la
+
+kio_ldap_la_SOURCES = kio_ldap.cpp
+kio_ldap_la_LIBADD = $(LIB_KIO) $(LDAP_LIBS) $(LIB_KABC)
+kio_ldap_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LDAP_RPATH) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = kio_ldap.h
+
+kdelnk_DATA = ldap.protocol ldaps.protocol
+kdelnkdir = $(kde_servicesdir)
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_ldap.pot
diff --git a/kioslave/ldap/configure.in.in b/kioslave/ldap/configure.in.in
new file mode 100644
index 000000000..b23c12387
--- /dev/null
+++ b/kioslave/ldap/configure.in.in
@@ -0,0 +1,111 @@
+AC_MSG_CHECKING(for LDAP support)
+AC_ARG_WITH(ldap,
+AC_HELP_STRING([--with-ldap=PATH],[Set path for LDAP files [default=check]]),
+[ case "$withval" in
+ yes)
+ with_ldap=CHECK
+ ;;
+ esac ],
+[ with_ldap=CHECK ]
+)dnl
+
+if test "x$with_ldap" = "xCHECK" ; then
+ with_ldap=NOTFOUND
+ search_incs="$kde_includes /usr/include /usr/local/include"
+ AC_FIND_FILE(ldap.h, $search_incs, ldap_incdir)
+ if test -r $ldap_incdir/ldap.h ; then
+ test "x$ldap_incdir" != "x/usr/include" && LDAP_INCS="-I$ldap_incdir"
+ with_ldap=FOUND
+ fi
+ if test $with_ldap = FOUND ; then
+ with_ldap=NOTFOUND
+ for ext in la so sl a dylib ; do
+ AC_FIND_FILE(libldap.$ext, $kde_libraries /usr/lib /usr/local/lib /usr/lib64,
+ ldap_libdir)
+ if test -r $ldap_libdir/libldap.$ext ; then
+ if test "x$ldap_libdir" != "x/usr/lib" ; then
+ LDAP_LIBS="-L$ldap_libdir "
+ test "$USE_RPATH" = yes && LDAP_RPATH="-R $ldap_libdir"
+ fi
+ LDAP_LIBS="${LDAP_LIBS}-lldap"
+ with_ldap=FOUND
+ break
+ fi
+ done
+ fi
+fi
+
+case "$with_ldap" in
+no) AC_MSG_RESULT(no) ;;
+framework)
+ LDAP_LIBS="-Xlinker -framework -Xlinker LDAP"
+ AC_DEFINE_UNQUOTED(HAVE_LIBLDAP, 1, [Define if you have LDAP libraries])
+ LDAP_SUBDIR="ldap"
+ AC_MSG_RESULT(Apple framework)
+ ;;
+FOUND)
+ AC_MSG_RESULT(incs=$ldap_incdir libs=$ldap_libdir)
+ ;;
+NOTFOUND) AC_MSG_RESULT(searched but not found) ;;
+*)
+ AC_MSG_RESULT($with_ldap)
+ ;;
+esac
+
+LIB_LBER=
+KDE_CHECK_LIB(lber, ber_alloc, [LIB_LBER=-llber], [], -L$ldap_libdir)
+AC_SUBST(LIB_LBER)
+
+AC_MSG_CHECKING(whether LDAP support can be compiled)
+
+ if test "x$with_ldap" != "xFOUND" ; then
+ LDAP_ROOT="$with_ldap"
+ if test "x$LDAP_ROOT" != "x/usr" ; then
+ LDAP_INCS="-I${LDAP_ROOT}/include"
+ LDAP_LIBS="-L${LDAP_ROOT}/lib "
+ if test "$USE_RPATH" = "yes" ; then
+ LDAP_RPATH="-R ${LDAP_ROOT}/lib"
+ fi
+ fi
+ LDAP_LIBS="${LDAP_LIBS}-lldap"
+ fi
+ LDAP_LIBS="${LDAP_LIBS} ${LIB_LBER} ${LIBRESOLV}"
+
+ kde_safe_LIBS="$LIBS"
+ kde_safe_CFLAGS="$CFLAGS"
+ LIBS="$LIBS $all_libraries $LDAP_LIBS $KRB4_LIBS $X_EXTRA_LIBS"
+ CFLAGS="$CFLAGS $all_includes $LDAP_INCS $KRB4_INCS"
+ AC_LANG_SAVE
+ AC_LANG_C
+ AC_TRY_LINK(dnl
+ [
+ #include <ldap.h>
+ #if LDAP_API_VERSION < 2004
+ #error LDAP version too old, please upgrade to a library supporting API 2004 or higher
+ #endif
+ ],
+ [
+ LDAP *ldap;
+ ],
+ , with_ldap=no
+ )
+ AC_LANG_RESTORE
+ CFLAGS=$kde_safe_CFLAGS
+ LIBS=$kde_safe_LIBS
+ if test "$with_ldap" = "no" ; then
+ LDAP_INCS=
+ LDAP_LIBS=
+ LDAP_RPATH=
+ LDAP_SUBDIR=
+ AC_MSG_RESULT(no (but first try gave $msg))
+ else
+ AC_DEFINE_UNQUOTED(HAVE_LIBLDAP, 1, [Define if you have LDAP libraries])
+ LDAP_SUBDIR="ldap"
+ AC_MSG_RESULT(yes)
+ fi
+
+AC_SUBST(LDAP_INCS)
+AC_SUBST(LDAP_LIBS)
+AC_SUBST(LDAP_RPATH)
+
+AM_CONDITIONAL(include_kioslave_ldap, test -n "$LDAP_SUBDIR")
diff --git a/kioslave/ldap/kio_ldap.cpp b/kioslave/ldap/kio_ldap.cpp
new file mode 100644
index 000000000..749ab6121
--- /dev/null
+++ b/kioslave/ldap/kio_ldap.cpp
@@ -0,0 +1,1154 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <kdebug.h>
+#include <kinstance.h>
+#include <klocale.h>
+
+#ifdef HAVE_SASL_SASL_H //prefer libsasl2
+#include <sasl/sasl.h>
+#else
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#endif
+#endif
+#include <kabc/ldif.h>
+
+#include "kio_ldap.h"
+
+using namespace KIO;
+using namespace KABC;
+
+extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); }
+
+/**
+ * The main program.
+ */
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_ldap" );
+
+ kdDebug(7125) << "Starting " << getpid() << endl;
+
+ if ( argc != 4 ) {
+ kdError() << "Usage kio_ldap protocol pool app" << endl;
+ return -1;
+ }
+
+ // let the protocol class do its work
+ LDAPProtocol slave( argv[1], argv[ 2 ], argv[ 3 ] );
+ slave.dispatchLoop();
+
+ kdDebug( 7125 ) << "Done" << endl;
+ return 0;
+}
+
+/**
+ * Initialize the ldap slave
+ */
+LDAPProtocol::LDAPProtocol( const QCString &protocol, const QCString &pool,
+ const QCString &app ) : SlaveBase( protocol, pool, app )
+{
+ mLDAP = 0; mTLS = 0; mVer = 3; mAuthSASL = false;
+ mRealm = ""; mBindName = "";
+ mTimeLimit = mSizeLimit = 0;
+ kdDebug(7125) << "LDAPProtocol::LDAPProtocol (" << protocol << ")" << endl;
+}
+
+LDAPProtocol::~LDAPProtocol()
+{
+ closeConnection();
+}
+
+void LDAPProtocol::LDAPErr( const KURL &url, int err )
+{
+
+ char *errmsg = 0;
+ if ( mLDAP ) {
+ if ( err == LDAP_SUCCESS ) ldap_get_option( mLDAP, LDAP_OPT_ERROR_NUMBER, &err );
+ if ( err != LDAP_SUCCESS ) ldap_get_option( mLDAP, LDAP_OPT_ERROR_STRING, &errmsg );
+ }
+ if ( err == LDAP_SUCCESS ) return;
+ kdDebug(7125) << "error code: " << err << " msg: " << ldap_err2string(err) <<
+ " Additonal error message: '" << errmsg << "'" << endl;
+ QString msg;
+ QString extraMsg;
+ if ( errmsg ) {
+ if ( errmsg[0] )
+ extraMsg = i18n("\nAdditional info: ") + QString::fromUtf8( errmsg );
+ free( errmsg );
+ }
+ msg = url.prettyURL();
+ if ( !extraMsg.isEmpty() ) msg += extraMsg;
+
+ /* FIXME: No need to close on all errors */
+ closeConnection();
+
+ switch (err) {
+/* FIXME: is it worth mapping the following error codes to kio errors?
+
+ LDAP_OPERATIONS_ERROR
+ LDAP_STRONG_AUTH_REQUIRED
+ LDAP_PROTOCOL_ERROR
+ LDAP_TIMELIMIT_EXCEEDED
+ LDAP_SIZELIMIT_EXCEEDED
+ LDAP_COMPARE_FALSE
+ LDAP_COMPARE_TRUE
+ LDAP_PARTIAL_RESULTS
+ LDAP_NO_SUCH_ATTRIBUTE
+ LDAP_UNDEFINED_TYPE
+ LDAP_INAPPROPRIATE_MATCHING
+ LDAP_CONSTRAINT_VIOLATION
+ LDAP_INVALID_SYNTAX
+ LDAP_NO_SUCH_OBJECT
+ LDAP_ALIAS_PROBLEM
+ LDAP_INVALID_DN_SYNTAX
+ LDAP_IS_LEAF
+ LDAP_ALIAS_DEREF_PROBLEM
+ LDAP_INAPPROPRIATE_AUTH
+ LDAP_BUSY
+ LDAP_UNAVAILABLE
+ LDAP_UNWILLING_TO_PERFORM
+ LDAP_LOOP_DETECT
+ LDAP_NAMING_VIOLATION
+ LDAP_OBJECT_CLASS_VIOLATION
+ LDAP_NOT_ALLOWED_ON_NONLEAF
+ LDAP_NOT_ALLOWED_ON_RDN
+ LDAP_NO_OBJECT_CLASS_MODS
+ LDAP_OTHER
+ LDAP_LOCAL_ERROR
+ LDAP_ENCODING_ERROR
+ LDAP_DECODING_ERROR
+ LDAP_FILTER_ERROR
+*/
+ case LDAP_AUTH_UNKNOWN:
+ case LDAP_INVALID_CREDENTIALS:
+ case LDAP_STRONG_AUTH_NOT_SUPPORTED:
+ error(ERR_COULD_NOT_AUTHENTICATE, msg);
+ break;
+ case LDAP_ALREADY_EXISTS:
+ error(ERR_FILE_ALREADY_EXIST, msg);
+ break;
+ case LDAP_INSUFFICIENT_ACCESS:
+ error(ERR_ACCESS_DENIED, msg);
+ break;
+ case LDAP_CONNECT_ERROR:
+ case LDAP_SERVER_DOWN:
+ error(ERR_COULD_NOT_CONNECT,msg);
+ break;
+ case LDAP_TIMEOUT:
+ error(ERR_SERVER_TIMEOUT,msg);
+ break;
+ case LDAP_PARAM_ERROR:
+ error(ERR_INTERNAL,msg);
+ break;
+ case LDAP_NO_MEMORY:
+ error(ERR_OUT_OF_MEMORY,msg);
+ break;
+
+ default:
+ error( ERR_SLAVE_DEFINED,
+ i18n( "LDAP server returned the error: %1 %2\nThe LDAP URL was: %3" ).
+ arg( ldap_err2string(err)).arg( extraMsg ).arg( url.prettyURL() ) );
+ }
+}
+
+void LDAPProtocol::controlsFromMetaData( LDAPControl ***serverctrls,
+ LDAPControl ***clientctrls )
+{
+ QString oid; bool critical; QByteArray value;
+ int i = 0;
+ while ( hasMetaData( QString::fromLatin1("SERVER_CTRL%1").arg(i) ) ) {
+ QCString val = metaData( QString::fromLatin1("SERVER_CTRL%1").arg(i) ).utf8();
+ LDIF::splitControl( val, oid, critical, value );
+ kdDebug(7125) << "server ctrl #" << i << " value: " << val <<
+ " oid: " << oid << " critical: " << critical << " value: " <<
+ QString::fromUtf8( value, value.size() ) << endl;
+ addControlOp( serverctrls, oid, value, critical );
+ i++;
+ }
+ i = 0;
+ while ( hasMetaData( QString::fromLatin1("CLIENT_CTRL%1").arg(i) ) ) {
+ QCString val = metaData( QString::fromLatin1("CLIENT_CTRL%1").arg(i) ).utf8();
+ LDIF::splitControl( val, oid, critical, value );
+ kdDebug(7125) << "client ctrl #" << i << " value: " << val <<
+ " oid: " << oid << " critical: " << critical << " value: " <<
+ QString::fromUtf8( value, value.size() ) << endl;
+ addControlOp( clientctrls, oid, value, critical );
+ i++;
+ }
+}
+
+int LDAPProtocol::asyncSearch( LDAPUrl &usrc )
+{
+ char **attrs = 0;
+ int msgid;
+ LDAPControl **serverctrls = 0, **clientctrls = 0;
+
+ int count = usrc.attributes().count();
+ if ( count > 0 ) {
+ attrs = static_cast<char**>( malloc((count+1) * sizeof(char*)) );
+ for (int i=0; i<count; i++)
+ attrs[i] = strdup( (*usrc.attributes().at(i)).utf8() );
+ attrs[count] = 0;
+ }
+
+ int retval, scope = LDAP_SCOPE_BASE;
+ switch ( usrc.scope() ) {
+ case LDAPUrl::Base:
+ scope = LDAP_SCOPE_BASE;
+ break;
+ case LDAPUrl::One:
+ scope = LDAP_SCOPE_ONELEVEL;
+ break;
+ case LDAPUrl::Sub:
+ scope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+
+ controlsFromMetaData( &serverctrls, &clientctrls );
+
+ kdDebug(7125) << "asyncSearch() dn=\"" << usrc.dn() << "\" scope=" <<
+ usrc.scope() << " filter=\"" << usrc.filter() << "\" attrs=" << usrc.attributes() <<
+ endl;
+ retval = ldap_search_ext( mLDAP, usrc.dn().utf8(), scope,
+ usrc.filter().isEmpty() ? QCString() : usrc.filter().utf8(), attrs, 0,
+ serverctrls, clientctrls,
+ 0, mSizeLimit, &msgid );
+
+ ldap_controls_free( serverctrls );
+ ldap_controls_free( clientctrls );
+
+ // free the attributes list again
+ if ( count > 0 ) {
+ for ( int i=0; i<count; i++ ) free( attrs[i] );
+ free(attrs);
+ }
+
+ if ( retval == 0 ) retval = msgid;
+ return retval;
+}
+
+QCString LDAPProtocol::LDAPEntryAsLDIF( LDAPMessage *message )
+{
+ QCString result;
+ char *name;
+ struct berval **bvals;
+ BerElement *entry;
+ QByteArray tmp;
+
+ char *dn = ldap_get_dn( mLDAP, message );
+ if ( dn == NULL ) return QCString( "" );
+ tmp.setRawData( dn, strlen( dn ) );
+ result += LDIF::assembleLine( "dn", tmp ) + '\n';
+ tmp.resetRawData( dn, strlen( dn ) );
+ ldap_memfree( dn );
+
+ // iterate over the attributes
+ name = ldap_first_attribute(mLDAP, message, &entry);
+ while ( name != 0 )
+ {
+ // print the values
+ bvals = ldap_get_values_len(mLDAP, message, name);
+ if ( bvals ) {
+
+ for ( int i = 0; bvals[i] != 0; i++ ) {
+ char* val = bvals[i]->bv_val;
+ unsigned long len = bvals[i]->bv_len;
+ tmp.setRawData( val, len );
+ result += LDIF::assembleLine( QString::fromUtf8( name ), tmp, 76 ) + '\n';
+ tmp.resetRawData( val, len );
+ }
+ ldap_value_free_len(bvals);
+ }
+ ldap_memfree( name );
+ // next attribute
+ name = ldap_next_attribute(mLDAP, message, entry);
+ }
+ return result;
+}
+
+void LDAPProtocol::addControlOp( LDAPControl ***pctrls, const QString &oid,
+ const QByteArray &value, bool critical )
+{
+ LDAPControl **ctrls;
+ LDAPControl *ctrl = (LDAPControl *) malloc( sizeof( LDAPControl ) );
+
+ ctrls = *pctrls;
+
+ kdDebug(7125) << "addControlOp: oid:'" << oid << "' val: '" <<
+ QString::fromUtf8(value, value.size()) << "'" << endl;
+ int vallen = value.size();
+ ctrl->ldctl_value.bv_len = vallen;
+ if ( vallen ) {
+ ctrl->ldctl_value.bv_val = (char*) malloc( vallen );
+ memcpy( ctrl->ldctl_value.bv_val, value.data(), vallen );
+ } else {
+ ctrl->ldctl_value.bv_val = 0;
+ }
+ ctrl->ldctl_iscritical = critical;
+ ctrl->ldctl_oid = strdup( oid.utf8() );
+
+ uint i = 0;
+
+ if ( ctrls == 0 ) {
+ ctrls = (LDAPControl **) malloc ( 2 * sizeof( LDAPControl* ) );
+ ctrls[ 0 ] = 0;
+ ctrls[ 1 ] = 0;
+ } else {
+ while ( ctrls[ i ] != 0 ) i++;
+ ctrls[ i + 1 ] = 0;
+ ctrls = (LDAPControl **) realloc( ctrls, (i + 2) * sizeof( LDAPControl * ) );
+ }
+ ctrls[ i ] = ctrl;
+
+ *pctrls = ctrls;
+}
+
+void LDAPProtocol::addModOp( LDAPMod ***pmods, int mod_type, const QString &attr,
+ const QByteArray &value )
+{
+// kdDebug(7125) << "type: " << mod_type << " attr: " << attr <<
+// " value: " << QString::fromUtf8(value,value.size()) <<
+// " size: " << value.size() << endl;
+ LDAPMod **mods;
+
+ mods = *pmods;
+
+ uint i = 0;
+
+ if ( mods == 0 ) {
+ mods = (LDAPMod **) malloc ( 2 * sizeof( LDAPMod* ) );
+ mods[ 0 ] = (LDAPMod*) malloc( sizeof( LDAPMod ) );
+ mods[ 1 ] = 0;
+ memset( mods[ 0 ], 0, sizeof( LDAPMod ) );
+ } else {
+ while( mods[ i ] != 0 &&
+ ( strcmp( attr.utf8(),mods[i]->mod_type ) != 0 ||
+ ( mods[ i ]->mod_op & ~LDAP_MOD_BVALUES ) != mod_type ) ) i++;
+
+ if ( mods[ i ] == 0 ) {
+ mods = ( LDAPMod ** )realloc( mods, (i + 2) * sizeof( LDAPMod * ) );
+ if ( mods == 0 ) {
+ kdError() << "addModOp: realloc" << endl;
+ return;
+ }
+ mods[ i + 1 ] = 0;
+ mods[ i ] = ( LDAPMod* ) malloc( sizeof( LDAPMod ) );
+ memset( mods[ i ], 0, sizeof( LDAPMod ) );
+ }
+ }
+
+ mods[ i ]->mod_op = mod_type | LDAP_MOD_BVALUES;
+ if ( mods[ i ]->mod_type == 0 ) mods[ i ]->mod_type = strdup( attr.utf8() );
+
+ *pmods = mods;
+
+ int vallen = value.size();
+ if ( vallen == 0 ) return;
+ BerValue *berval;
+ berval = ( BerValue* ) malloc( sizeof( BerValue ) );
+ berval -> bv_val = (char*) malloc( vallen );
+ berval -> bv_len = vallen;
+ memcpy( berval -> bv_val, value.data(), vallen );
+
+ if ( mods[ i ] -> mod_vals.modv_bvals == 0 ) {
+ mods[ i ]->mod_vals.modv_bvals = ( BerValue** ) malloc( sizeof( BerValue* ) * 2 );
+ mods[ i ]->mod_vals.modv_bvals[ 0 ] = berval;
+ mods[ i ]->mod_vals.modv_bvals[ 1 ] = 0;
+ kdDebug(7125) << "addModOp: new bervalue struct " << endl;
+ } else {
+ uint j = 0;
+ while ( mods[ i ]->mod_vals.modv_bvals[ j ] != 0 ) j++;
+ mods[ i ]->mod_vals.modv_bvals = ( BerValue ** )
+ realloc( mods[ i ]->mod_vals.modv_bvals, (j + 2) * sizeof( BerValue* ) );
+ if ( mods[ i ]->mod_vals.modv_bvals == 0 ) {
+ kdError() << "addModOp: realloc" << endl;
+ return;
+ }
+ mods[ i ]->mod_vals.modv_bvals[ j ] = berval;
+ mods[ i ]->mod_vals.modv_bvals[ j+1 ] = 0;
+ kdDebug(7125) << j << ". new bervalue " << endl;
+ }
+}
+
+void LDAPProtocol::LDAPEntry2UDSEntry( const QString &dn, UDSEntry &entry,
+ const LDAPUrl &usrc, bool dir )
+{
+ UDSAtom atom;
+
+ int pos;
+ entry.clear();
+ atom.m_uds = UDS_NAME;
+ atom.m_long = 0;
+ QString name = dn;
+ if ( (pos = name.find(",")) > 0 )
+ name = name.left( pos );
+ if ( (pos = name.find("=")) > 0 )
+ name.remove( 0, pos+1 );
+ name.replace(' ', "_");
+ if ( !dir ) name += ".ldif";
+ atom.m_str = name;
+ entry.append( atom );
+
+ // the file type
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = "";
+ atom.m_long = dir ? S_IFDIR : S_IFREG;
+ entry.append( atom );
+
+ // the mimetype
+ if (!dir) {
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_long = 0;
+ atom.m_str = "text/plain";
+ entry.append( atom );
+ }
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = dir ? 0500 : 0400;
+ entry.append( atom );
+
+ // the url
+ atom.m_uds = UDS_URL;
+ atom.m_long = 0;
+ LDAPUrl url;
+ url=usrc;
+
+ url.setPath("/"+dn);
+ url.setScope( dir ? LDAPUrl::One : LDAPUrl::Base );
+ atom.m_str = url.prettyURL();
+ entry.append( atom );
+}
+
+void LDAPProtocol::changeCheck( LDAPUrl &url )
+{
+ bool critical;
+ bool tls = ( url.hasExtension( "x-tls" ) );
+ int ver = 3;
+ if ( url.hasExtension( "x-ver" ) )
+ ver = url.extension( "x-ver", critical).toInt();
+ bool authSASL = url.hasExtension( "x-sasl" );
+ QString mech;
+ if ( url.hasExtension( "x-mech" ) )
+ mech = url.extension( "x-mech", critical).upper();
+ QString realm;
+ if ( url.hasExtension( "x-realm" ) )
+ mech = url.extension( "x-realm", critical).upper();
+ QString bindname;
+ if ( url.hasExtension( "bindname" ) )
+ bindname = url.extension( "bindname", critical).upper();
+ int timelimit = 0;
+ if ( url.hasExtension( "x-timelimit" ) )
+ timelimit = url.extension( "x-timelimit", critical).toInt();
+ int sizelimit = 0;
+ if ( url.hasExtension( "x-sizelimit" ) )
+ sizelimit = url.extension( "x-sizelimit", critical).toInt();
+
+ if ( !authSASL && bindname.isEmpty() ) bindname = mUser;
+
+ if ( tls != mTLS || ver != mVer || authSASL != mAuthSASL || mech != mMech ||
+ mRealm != realm || mBindName != bindname || mTimeLimit != timelimit ||
+ mSizeLimit != sizelimit ) {
+ closeConnection();
+ mTLS = tls;
+ mVer = ver;
+ mAuthSASL = authSASL;
+ mMech = mech;
+ mRealm = realm;
+ mBindName = bindname;
+ mTimeLimit = timelimit;
+ mSizeLimit = sizelimit;
+ kdDebug(7125) << "parameters changed: tls = " << mTLS <<
+ " version: " << mVer << "SASLauth: " << mAuthSASL << endl;
+ openConnection();
+ if ( mAuthSASL ) {
+ url.setUser( mUser );
+ } else {
+ url.setUser( mBindName );
+ }
+ } else {
+ if ( !mLDAP ) openConnection();
+ }
+}
+
+void LDAPProtocol::setHost( const QString& host, int port,
+ const QString& user, const QString& password )
+{
+
+ if( mHost != host || mPort != port || mUser != user || mPassword != password )
+ closeConnection();
+
+ mHost = host;
+ if( port > 0 )
+ mPort = port;
+ else {
+ struct servent *pse;
+ if ( (pse = getservbyname(mProtocol, "tcp")) == NULL )
+ if ( mProtocol == "ldaps" )
+ mPort = 636;
+ else
+ mPort = 389;
+ else
+ mPort = ntohs( pse->s_port );
+ }
+ mUser = user;
+ mPassword = password;
+
+ kdDebug(7125) << "setHost: " << host << " port: " << port << " user: " <<
+ mUser << " pass: [protected]" << endl;
+}
+
+static int kldap_sasl_interact( LDAP *, unsigned, void *slave, void *in )
+{
+ return ((LDAPProtocol*) slave)->saslInteract( in );
+}
+
+void LDAPProtocol::fillAuthInfo( AuthInfo &info )
+{
+ info.url.setProtocol( mProtocol );
+ info.url.setHost( mHost );
+ info.url.setPort( mPort );
+ info.url.setUser( mUser );
+ info.caption = i18n("LDAP Login");
+ info.comment = QString::fromLatin1( mProtocol ) + "://" + mHost + ":" +
+ QString::number( mPort );
+ info.commentLabel = i18n("site:");
+ info.username = mAuthSASL ? mUser : mBindName;
+ info.password = mPassword;
+ info.keepPassword = true;
+}
+
+int LDAPProtocol::saslInteract( void *in )
+{
+#if defined HAVE_SASL_H || defined HAVE_SASL_SASL_H
+ AuthInfo info;
+ fillAuthInfo( info );
+
+ sasl_interact_t *interact = ( sasl_interact_t * ) in;
+
+ //some mechanisms do not require username && pass, so it doesn't need a popup
+ //window for getting this info
+ for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
+ if ( interact->id == SASL_CB_AUTHNAME ||
+ interact->id == SASL_CB_PASS ) {
+
+ if ( info.username.isEmpty() || info.password.isEmpty() ) {
+
+ const bool cached = checkCachedAuthentication( info );
+
+ if ( ! ( ( mFirstAuth && cached ) ||
+ ( mFirstAuth ?
+ openPassDlg( info ) :
+ openPassDlg( info, i18n("Invalid authorization information.") ) ) ) ) {
+ kdDebug(7125) << "Dialog cancelled!" << endl;
+ mCancel = true;
+ return LDAP_USER_CANCELLED;
+ }
+ mUser = info.username;
+ mPassword = info.password;
+ }
+ break;
+ }
+ }
+
+ interact = ( sasl_interact_t * ) in;
+ QString value;
+
+ while( interact->id != SASL_CB_LIST_END ) {
+ value = "";
+ switch( interact->id ) {
+ case SASL_CB_GETREALM:
+ value = mRealm;
+ kdDebug(7125) << "SASL_REALM=" << mRealm << endl;
+ break;
+ case SASL_CB_AUTHNAME:
+ value = mUser;
+ kdDebug(7125) << "SASL_AUTHNAME=" << mUser << endl;
+ break;
+ case SASL_CB_PASS:
+ value = mPassword;
+ kdDebug(7125) << "SASL_PASSWD=[hidden]" << endl;
+ break;
+ case SASL_CB_USER:
+ value = mBindName;
+ kdDebug(7125) << "SASL_AUTHZID=" << mBindName << endl;
+ break;
+ }
+ if ( value.isEmpty() ) {
+ interact->result = NULL;
+ interact->len = 0;
+ } else {
+ interact->result = strdup( value.utf8() );
+ interact->len = strlen( (const char *) interact->result );
+ }
+ interact++;
+ }
+
+#endif
+ return LDAP_SUCCESS;
+}
+
+void LDAPProtocol::openConnection()
+{
+ if ( mLDAP ) return;
+
+ int version,ret;
+
+ version = ( mVer == 2 ) ? LDAP_VERSION2 : LDAP_VERSION3;
+
+ KURL Url;
+ Url.setProtocol( mProtocol );
+ Url.setHost( mHost );
+ Url.setPort( mPort );
+
+ AuthInfo info;
+ fillAuthInfo( info );
+///////////////////////////////////////////////////////////////////////////
+ kdDebug(7125) << "OpenConnection to " << mHost << ":" << mPort << endl;
+
+ ret = ldap_initialize( &mLDAP, Url.htmlURL().utf8() );
+ if ( ret != LDAP_SUCCESS ) {
+ LDAPErr( Url, ret );
+ return;
+ }
+
+ if ( (ldap_set_option( mLDAP, LDAP_OPT_PROTOCOL_VERSION, &version )) !=
+ LDAP_OPT_SUCCESS ) {
+
+ closeConnection();
+ error( ERR_UNSUPPORTED_ACTION,
+ i18n("Cannot set LDAP protocol version %1").arg(version) );
+ return;
+ }
+
+ if ( mTLS ) {
+ kdDebug(7125) << "start TLS" << endl;
+ if ( ( ret = ldap_start_tls_s( mLDAP, NULL, NULL ) ) != LDAP_SUCCESS ) {
+ LDAPErr( Url );
+ return;
+ }
+ }
+
+ if ( mSizeLimit ) {
+ kdDebug(7125) << "sizelimit: " << mSizeLimit << endl;
+ if ( ldap_set_option( mLDAP, LDAP_OPT_SIZELIMIT, &mSizeLimit ) != LDAP_SUCCESS ) {
+ closeConnection();
+ error( ERR_UNSUPPORTED_ACTION,
+ i18n("Cannot set size limit."));
+ return;
+ }
+ }
+
+ if ( mTimeLimit ) {
+ kdDebug(7125) << "timelimit: " << mTimeLimit << endl;
+ if ( ldap_set_option( mLDAP, LDAP_OPT_TIMELIMIT, &mTimeLimit ) != LDAP_SUCCESS ) {
+ closeConnection();
+ error( ERR_UNSUPPORTED_ACTION,
+ i18n("Cannot set time limit."));
+ return;
+ }
+ }
+
+#if !defined HAVE_SASL_H && !defined HAVE_SASL_SASL_H
+ if ( mAuthSASL ) {
+ closeConnection();
+ error( ERR_SLAVE_DEFINED,
+ i18n("SASL authentication not compiled into the ldap ioslave.") );
+ return;
+ }
+#endif
+
+ bool auth = false;
+ QString mechanism = mMech.isEmpty() ? "DIGEST-MD5" : mMech;
+ mFirstAuth = true; mCancel = false;
+
+ const bool cached = checkCachedAuthentication( info );
+
+ ret = LDAP_SUCCESS;
+ while (!auth) {
+ if ( !mAuthSASL && (
+ ( mFirstAuth &&
+ !( mBindName.isEmpty() && mPassword.isEmpty() ) && //For anonymous bind
+ ( mBindName.isEmpty() || mPassword.isEmpty() ) ) || !mFirstAuth ) )
+ {
+ if ( ( mFirstAuth && cached ) ||
+ ( mFirstAuth ?
+ openPassDlg( info ) :
+ openPassDlg( info, i18n("Invalid authorization information.") ) ) ) {
+
+ mBindName = info.username;
+ mPassword = info.password;
+ } else {
+ kdDebug(7125) << "Dialog cancelled!" << endl;
+ error( ERR_USER_CANCELED, QString::null );
+ closeConnection();
+ return;
+ }
+ }
+ kdDebug(7125) << "user: " << mUser << " bindname: " << mBindName << endl;
+ ret =
+#if defined HAVE_SASL_H || defined HAVE_SASL_SASL_H
+ mAuthSASL ?
+ ldap_sasl_interactive_bind_s( mLDAP, NULL, mechanism.utf8(),
+ NULL, NULL, LDAP_SASL_INTERACTIVE, &kldap_sasl_interact, this ) :
+#endif
+ ldap_simple_bind_s( mLDAP, mBindName.utf8(), mPassword.utf8() );
+
+ mFirstAuth = false;
+ if ( ret != LDAP_INVALID_CREDENTIALS &&
+ ret != LDAP_INSUFFICIENT_ACCESS &&
+ ret != LDAP_INAPPROPRIATE_AUTH ) {
+ kdDebug(7125) << "ldap_bind retval: " << ret << endl;
+ auth = true;
+ if ( ret != LDAP_SUCCESS ) {
+ if ( mCancel )
+ error( ERR_USER_CANCELED, QString::null );
+ else
+ LDAPErr( Url );
+ closeConnection();
+ return;
+ }
+ }
+ }
+
+ kdDebug(7125) << "connected!" << endl;
+ connected();
+}
+
+void LDAPProtocol::closeConnection()
+{
+ if (mLDAP) ldap_unbind(mLDAP);
+ mLDAP = 0;
+ kdDebug(7125) << "connection closed!" << endl;
+}
+
+/**
+ * Get the information contained in the URL.
+ */
+void LDAPProtocol::get( const KURL &_url )
+{
+ kdDebug(7125) << "get(" << _url << ")" << endl;
+
+ LDAPUrl usrc(_url);
+ int ret, id;
+ LDAPMessage *msg,*entry;
+
+ changeCheck( usrc );
+ if ( !mLDAP ) {
+ finished();
+ return;
+ }
+
+ if ( (id = asyncSearch( usrc )) == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+
+ // tell the mimetype
+ mimeType("text/plain");
+ // collect the result
+ QCString result;
+ filesize_t processed_size = 0;
+ QByteArray array;
+
+ while( true ) {
+ ret = ldap_result( mLDAP, id, 0, NULL, &msg );
+ if ( ret == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+ kdDebug(7125) << " ldap_result: " << ret << endl;
+ if ( ret == LDAP_RES_SEARCH_RESULT ) break;
+ if ( ret != LDAP_RES_SEARCH_ENTRY ) continue;
+
+ entry = ldap_first_entry( mLDAP, msg );
+ while ( entry ) {
+ result = LDAPEntryAsLDIF(entry);
+ result += '\n';
+ uint len = result.length();
+ processed_size += len;
+ array.setRawData( result.data(), len );
+ data(array);
+ processedSize( processed_size );
+ array.resetRawData( result.data(), len );
+
+ entry = ldap_next_entry( mLDAP, entry );
+ }
+ LDAPErr( _url );
+
+ ldap_msgfree(msg);
+ // tell the length
+ }
+
+ totalSize(processed_size);
+
+ array.resize(0);
+ // tell we are finished
+ data(array);
+
+ // tell we are finished
+ finished();
+}
+
+/**
+ * Test if the url contains a directory or a file.
+ */
+void LDAPProtocol::stat( const KURL &_url )
+{
+ kdDebug(7125) << "stat(" << _url << ")" << endl;
+
+ QStringList att,saveatt;
+ LDAPUrl usrc(_url);
+ LDAPMessage *msg;
+ int ret, id;
+
+ changeCheck( usrc );
+ if ( !mLDAP ) {
+ finished();
+ return;
+ }
+
+ // look how many entries match
+ saveatt = usrc.attributes();
+ att.append( "dn" );
+ usrc.setAttributes( att );
+ if ( _url.query().isEmpty() ) usrc.setScope( LDAPUrl::One );
+
+ if ( (id = asyncSearch( usrc )) == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+
+ kdDebug(7125) << "stat() getting result" << endl;
+ do {
+ ret = ldap_result( mLDAP, id, 0, NULL, &msg );
+ if ( ret == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+ if ( ret == LDAP_RES_SEARCH_RESULT ) {
+ ldap_msgfree( msg );
+ error( ERR_DOES_NOT_EXIST, _url.prettyURL() );
+ return;
+ }
+ } while ( ret != LDAP_RES_SEARCH_ENTRY );
+
+ ldap_msgfree( msg );
+ ldap_abandon( mLDAP, id );
+
+ usrc.setAttributes( saveatt );
+
+ UDSEntry uds;
+ bool critical;
+ LDAPEntry2UDSEntry( usrc.dn(), uds, usrc, usrc.extension("x-dir", critical) != "base" );
+
+ statEntry( uds );
+ // we are done
+ finished();
+}
+
+/**
+ * Deletes one entry;
+ */
+void LDAPProtocol::del( const KURL &_url, bool )
+{
+ kdDebug(7125) << "del(" << _url << ")" << endl;
+
+ LDAPUrl usrc(_url);
+ int ret;
+
+ changeCheck( usrc );
+ if ( !mLDAP ) {
+ finished();
+ return;
+ }
+
+ kdDebug(7125) << " del: " << usrc.dn().utf8() << endl ;
+
+ if ( (ret = ldap_delete_s( mLDAP,usrc.dn().utf8() )) != LDAP_SUCCESS ) {
+ LDAPErr( _url );
+ return;
+ }
+ finished();
+}
+
+#define FREELDAPMEM { \
+ ldap_mods_free( lmod, 1 ); \
+ ldap_controls_free( serverctrls ); \
+ ldap_controls_free( clientctrls ); \
+ lmod = 0; serverctrls = 0; clientctrls = 0; \
+ }
+
+void LDAPProtocol::put( const KURL &_url, int, bool overwrite, bool )
+{
+ kdDebug(7125) << "put(" << _url << ")" << endl;
+
+ LDAPUrl usrc(_url);
+
+ changeCheck( usrc );
+ if ( !mLDAP ) {
+ finished();
+ return;
+ }
+
+ LDAPMod **lmod = 0;
+ LDAPControl **serverctrls = 0, **clientctrls = 0;
+ QByteArray buffer;
+ int result = 0;
+ LDIF::ParseVal ret;
+ LDIF ldif;
+ ret = LDIF::MoreData;
+ int ldaperr;
+
+
+ do {
+ if ( ret == LDIF::MoreData ) {
+ dataReq(); // Request for data
+ result = readData( buffer );
+ ldif.setLDIF( buffer );
+ }
+ if ( result < 0 ) {
+ //error
+ FREELDAPMEM;
+ return;
+ }
+ if ( result == 0 ) {
+ kdDebug(7125) << "EOF!" << endl;
+ ldif.endLDIF();
+ }
+ do {
+
+ ret = ldif.nextItem();
+ kdDebug(7125) << "nextitem: " << ret << endl;
+
+ switch ( ret ) {
+ case LDIF::None:
+ case LDIF::NewEntry:
+ case LDIF::MoreData:
+ break;
+ case LDIF::EndEntry:
+ ldaperr = LDAP_SUCCESS;
+ switch ( ldif.entryType() ) {
+ case LDIF::Entry_None:
+ error( ERR_INTERNAL, i18n("The LDIF parser failed.") );
+ FREELDAPMEM;
+ return;
+ case LDIF::Entry_Del:
+ kdDebug(7125) << "kio_ldap_del" << endl;
+ controlsFromMetaData( &serverctrls, &clientctrls );
+ ldaperr = ldap_delete_ext_s( mLDAP, ldif.dn().utf8(),
+ serverctrls, clientctrls );
+ FREELDAPMEM;
+ break;
+ case LDIF::Entry_Modrdn:
+ kdDebug(7125) << "kio_ldap_modrdn olddn:" << ldif.dn() <<
+ " newRdn: " << ldif.newRdn() <<
+ " newSuperior: " << ldif.newSuperior() <<
+ " deloldrdn: " << ldif.delOldRdn() << endl;
+ controlsFromMetaData( &serverctrls, &clientctrls );
+ ldaperr = ldap_rename_s( mLDAP, ldif.dn().utf8(), ldif.newRdn().utf8(),
+ ldif.newSuperior().isEmpty() ? QCString() : ldif.newSuperior().utf8(),
+ ldif.delOldRdn(), serverctrls, clientctrls );
+
+ FREELDAPMEM;
+ break;
+ case LDIF::Entry_Mod:
+ kdDebug(7125) << "kio_ldap_mod" << endl;
+ if ( lmod ) {
+ controlsFromMetaData( &serverctrls, &clientctrls );
+ ldaperr = ldap_modify_ext_s( mLDAP, ldif.dn().utf8(), lmod,
+ serverctrls, clientctrls );
+ FREELDAPMEM;
+ }
+ break;
+ case LDIF::Entry_Add:
+ kdDebug(7125) << "kio_ldap_add " << ldif.dn() << endl;
+ if ( lmod ) {
+ controlsFromMetaData( &serverctrls, &clientctrls );
+ ldaperr = ldap_add_ext_s( mLDAP, ldif.dn().utf8(), lmod,
+ serverctrls, clientctrls );
+ if ( ldaperr == LDAP_ALREADY_EXISTS && overwrite ) {
+ kdDebug(7125) << ldif.dn() << " already exists, delete first" << endl;
+ ldaperr = ldap_delete_s( mLDAP, ldif.dn().utf8() );
+ if ( ldaperr == LDAP_SUCCESS )
+ ldaperr = ldap_add_ext_s( mLDAP, ldif.dn().utf8(), lmod,
+ serverctrls, clientctrls );
+ }
+ FREELDAPMEM;
+ }
+ break;
+ }
+ if ( ldaperr != LDAP_SUCCESS ) {
+ kdDebug(7125) << "put ldap error: " << ldap_err2string(ldaperr) << endl;
+ LDAPErr( _url );
+ FREELDAPMEM;
+ return;
+ }
+ break;
+ case LDIF::Item:
+ switch ( ldif.entryType() ) {
+ case LDIF::Entry_Mod: {
+ int modtype = 0;
+ switch ( ldif.modType() ) {
+ case LDIF::Mod_None:
+ modtype = 0;
+ break;
+ case LDIF::Mod_Add:
+ modtype = LDAP_MOD_ADD;
+ break;
+ case LDIF::Mod_Replace:
+ modtype = LDAP_MOD_REPLACE;
+ break;
+ case LDIF::Mod_Del:
+ modtype = LDAP_MOD_DELETE;
+ break;
+ }
+ addModOp( &lmod, modtype, ldif.attr(), ldif.val() );
+ break;
+ }
+ case LDIF::Entry_Add:
+ if ( ldif.val().size() > 0 )
+ addModOp( &lmod, 0, ldif.attr(), ldif.val() );
+ break;
+ default:
+ error( ERR_INTERNAL, i18n("The LDIF parser failed.") );
+ FREELDAPMEM;
+ return;
+ }
+ break;
+ case LDIF::Control:
+ addControlOp( &serverctrls, ldif.oid(), ldif.val(), ldif.critical() );
+ break;
+ case LDIF::Err:
+ error( ERR_SLAVE_DEFINED,
+ i18n( "Invalid LDIF file in line %1." ).arg( ldif.lineNo() ) );
+ FREELDAPMEM;
+ return;
+ }
+ } while ( ret != LDIF::MoreData );
+ } while ( result > 0 );
+
+ FREELDAPMEM;
+ finished();
+}
+
+/**
+ * List the contents of a directory.
+ */
+void LDAPProtocol::listDir( const KURL &_url )
+{
+ int ret, ret2, id, id2;
+ unsigned long total=0;
+ char *dn;
+ QStringList att,saveatt;
+ LDAPMessage *entry,*msg,*entry2,*msg2;
+ LDAPUrl usrc(_url),usrc2;
+ bool critical;
+ bool isSub = ( usrc.extension( "x-dir", critical ) == "sub" );
+
+ kdDebug(7125) << "listDir(" << _url << ")" << endl;
+
+ changeCheck( usrc );
+ if ( !mLDAP ) {
+ finished();
+ return;
+ }
+ usrc2 = usrc;
+
+ saveatt = usrc.attributes();
+ // look up the entries
+ if ( isSub ) {
+ att.append("dn");
+ usrc.setAttributes(att);
+ }
+ if ( _url.query().isEmpty() ) usrc.setScope( LDAPUrl::One );
+
+ if ( (id = asyncSearch( usrc )) == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+
+ usrc.setAttributes( "" );
+ usrc.setExtension( "x-dir", "base" );
+ // publish the results
+ UDSEntry uds;
+
+ while( true ) {
+ ret = ldap_result( mLDAP, id, 0, NULL, &msg );
+ if ( ret == -1 ) {
+ LDAPErr( _url );
+ return;
+ }
+ if ( ret == LDAP_RES_SEARCH_RESULT ) break;
+ if ( ret != LDAP_RES_SEARCH_ENTRY ) continue;
+ kdDebug(7125) << " ldap_result: " << ret << endl;
+
+ entry = ldap_first_entry( mLDAP, msg );
+ while( entry ) {
+
+ total++;
+ uds.clear();
+
+ dn = ldap_get_dn( mLDAP, entry );
+ kdDebug(7125) << "dn: " << dn << endl;
+ LDAPEntry2UDSEntry( QString::fromUtf8(dn), uds, usrc );
+ listEntry( uds, false );
+// processedSize( total );
+ kdDebug(7125) << " total: " << total << " " << usrc.prettyURL() << endl;
+
+ // publish the sub-directories (if dirmode==sub)
+ if ( isSub ) {
+ usrc2.setDn( QString::fromUtf8( dn ) );
+ usrc2.setScope( LDAPUrl::One );
+ usrc2.setAttributes( att );
+ usrc2.setFilter( QString::null );
+ kdDebug(7125) << "search2 " << dn << endl;
+ if ( (id2 = asyncSearch( usrc2 )) != -1 ) {
+ while ( true ) {
+ kdDebug(7125) << " next result " << endl;
+ ret2 = ldap_result( mLDAP, id2, 0, NULL, &msg2 );
+ if ( ret2 == -1 ) break;
+ if ( ret2 == LDAP_RES_SEARCH_RESULT ) {
+ ldap_msgfree( msg2 );
+ break;
+ }
+ if ( ret2 == LDAP_RES_SEARCH_ENTRY ) {
+ entry2=ldap_first_entry( mLDAP, msg2 );
+ if ( entry2 ) {
+ usrc2.setAttributes( saveatt );
+ usrc2.setFilter( usrc.filter() );
+ LDAPEntry2UDSEntry( QString::fromUtf8( dn ), uds, usrc2, true );
+ listEntry( uds, false );
+ total++;
+ }
+ ldap_msgfree( msg2 );
+ ldap_abandon( mLDAP, id2 );
+ break;
+ }
+ }
+ }
+ }
+ free( dn );
+
+ entry = ldap_next_entry( mLDAP, entry );
+ }
+ LDAPErr( _url );
+ ldap_msgfree( msg );
+ }
+
+// totalSize( total );
+
+ uds.clear();
+ listEntry( uds, true );
+ // we are done
+ finished();
+}
diff --git a/kioslave/ldap/kio_ldap.h b/kioslave/ldap/kio_ldap.h
new file mode 100644
index 000000000..ff722d345
--- /dev/null
+++ b/kioslave/ldap/kio_ldap.h
@@ -0,0 +1,65 @@
+#ifndef __LDAP_H__
+#define __LDAP_H__
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+#include <kio/slavebase.h>
+#include <kio/authinfo.h>
+
+#define LDAP_DEPRECATED 1 /* Needed for ldap_simple_bind_s with openldap >= 2.3.x */
+#include <lber.h>
+#include <ldap.h>
+#include <kabc/ldapurl.h>
+
+class LDAPProtocol : public KIO::SlaveBase
+{
+ public:
+ LDAPProtocol( const QCString &protocol, const QCString &pool, const QCString &app );
+ virtual ~LDAPProtocol();
+
+ virtual void setHost( const QString& host, int port,
+ const QString& user, const QString& pass );
+
+ virtual void openConnection();
+ virtual void closeConnection();
+
+ virtual void get( const KURL& url );
+ virtual void stat( const KURL& url );
+ virtual void listDir( const KURL& url );
+ virtual void del( const KURL& url, bool isfile );
+ virtual void put( const KURL& url, int permissions, bool overwrite, bool resume );
+
+ int saslInteract( void *in );
+
+ private:
+
+ QString mHost;
+ int mPort;
+ QString mUser;
+ QString mPassword;
+ LDAP *mLDAP;
+ int mVer, mSizeLimit, mTimeLimit;
+ bool mTLS;
+ bool mAuthSASL;
+ QString mMech,mRealm,mBindName;
+ bool mCancel, mFirstAuth;
+
+ void controlsFromMetaData( LDAPControl ***serverctrls,
+ LDAPControl ***clientctrls );
+ void addControlOp( LDAPControl ***pctrls, const QString &oid,
+ const QByteArray &value, bool critical );
+ void addModOp( LDAPMod ***pmods, int mod_type,
+ const QString &attr, const QByteArray &value );
+ void LDAPEntry2UDSEntry( const QString &dn, KIO::UDSEntry &entry,
+ const KABC::LDAPUrl &usrc, bool dir=false );
+ int asyncSearch( KABC::LDAPUrl &usrc );
+
+ QCString LDAPEntryAsLDIF( LDAPMessage *msg );
+ void LDAPErr( const KURL &url, int err = LDAP_SUCCESS );
+ void changeCheck( KABC::LDAPUrl &url );
+
+ void fillAuthInfo( KIO::AuthInfo &info );
+};
+
+#endif
diff --git a/kioslave/ldap/ldap.protocol b/kioslave/ldap/ldap.protocol
new file mode 100644
index 000000000..3ab0b7eb5
--- /dev/null
+++ b/kioslave/ldap/ldap.protocol
@@ -0,0 +1,17 @@
+[Protocol]
+exec=kio_ldap
+protocol=ldap
+input=none
+output=filesystem
+listing=Name,
+reading=true
+source=true
+writing=true
+#makedir=true
+deleting=true
+#linking=true
+#moving=true
+mimetype=text/plain
+determineMimetypeFromExtension=false
+DocPath=kioslave/ldap.html
+Icon=kaddressbook
diff --git a/kioslave/ldap/ldaps.protocol b/kioslave/ldap/ldaps.protocol
new file mode 100644
index 000000000..542faa597
--- /dev/null
+++ b/kioslave/ldap/ldaps.protocol
@@ -0,0 +1,17 @@
+[Protocol]
+exec=kio_ldap
+protocol=ldaps
+input=none
+output=filesystem
+listing=Name,
+reading=true
+source=true
+writing=true
+#makedir=true
+deleting=true
+#linking=true
+#moving=true
+mimetype=text/plain
+determineMimetypeFromExtension=false
+DocPath=kioslave/ldap.html
+Icon=kaddressbook
diff --git a/kioslave/mac/AUTHORS b/kioslave/mac/AUTHORS
new file mode 100644
index 000000000..78b940be4
--- /dev/null
+++ b/kioslave/mac/AUTHORS
@@ -0,0 +1 @@
+Jonathan Riddell, jr@jriddell.org
diff --git a/kioslave/mac/ChangeLog b/kioslave/mac/ChangeLog
new file mode 100644
index 000000000..6c8f647ce
--- /dev/null
+++ b/kioslave/mac/ChangeLog
@@ -0,0 +1,39 @@
+10 Feb 2002 - Jonathan Riddell <jr@jriddell.org>
+ - v1.0
+ - Nicer icon (thanks to ikons project)
+ - moved into KDE CVS kdenonbeta
+ - Everything seems to be stable, lets up the version number to prove me wrong
+
+1 Feb 2002 - Jonathan Riddell <jr@jriddell.org>
+ - v0.8
+ - Now displays hidden files
+ - Locked files are copied as read only
+ - Nice icon
+ - sources now use autoconf/automake
+ - Fixed regular expression which matches some files as directories
+ - Aliases now display as links
+
+26 Jan 2002 - Jonathan Riddell <jr@jriddell.org>
+ - v0.7
+ - Converts some HFS+ file types and application labels into mimetypes
+ - Added a SuSE Makefile
+ - Hopefully managed to get the SuSE RPMs working
+ - When copying files kio-mac now reports the amount progressed so
+ you can see how much has been copied
+ - Text files are now copies over in text mode by default
+
+24 Jan2002 - Jonathan Riddell <jr@jriddell.org>
+ - v0.6
+ - It can now read empty directories without complaining
+ - Fixed the way data was being passed back to KDE which corrupted some files
+ - Fixed Makefile a bit
+ - Now reports the modified date to as good an acuracy as hpls -l gives it
+ - Found a truly bizarre bug while doing the above which broke
+ things a lot less than it should have
+
+21 Jan 2002 - Jonathan Riddell <jr@jriddell.org>
+ - v0.5
+ - Initial release
+ - talks to hfs+ partitions using hptools
+ - surprisingly successful
+
diff --git a/kioslave/mac/Makefile.am b/kioslave/mac/Makefile.am
new file mode 100644
index 000000000..f2f0d97fb
--- /dev/null
+++ b/kioslave/mac/Makefile.am
@@ -0,0 +1,23 @@
+## Makfile.am for kio_mac
+
+INCLUDES= $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_mac.la
+
+kio_mac_la_SOURCES = kio_mac.cpp
+kio_mac_la_LIBADD = -lkio
+kio_mac_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+noinst_HEADERS = kio_mac.h
+
+kdelnk_DATA = mac.protocol
+kdelnkdir = $(kde_servicesdir)
+
+METASOURCES = AUTO
+KDE_ICON = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_mac.pot
diff --git a/kioslave/mac/README b/kioslave/mac/README
new file mode 100644
index 000000000..bb907dd9c
--- /dev/null
+++ b/kioslave/mac/README
@@ -0,0 +1,65 @@
+From the hfsplus man page:
+
+ "HFS+, also known as the Macintosh Extended Format, was
+ introduced by Apple Computer in 1998 with the release of
+ MacOS 8.1. It contains many improvements over the old HFS
+ file system, most notably the ability to allocate up to
+ 2^64 blocks, resulting in much more efficient storage of
+ many small files on large disks."
+
+This kio slave lets you read an HFS+ partition from konqueror
+or any other KDE file dialogue. It uses hfsplus tools so you will
+need these installed for it to work.
+
+TO INSTALL
+
+Read the INSTALL file.
+
+
+NOTES
+
+Just enter mac:/ into Konqueror and you should see the contents of
+your MacOS partition. Actually you'll probably get an error message
+saying you havn't specified the right partition. Enter something
+like mac:/?dev=/dev/hda2 to specify the partition (if you don't know
+which partition MacOS is on you can probably guess by changing hda2 to
+hda3 and so on or use the print command from mac-fdisk. The partition
+will be used the next time so you don't have to specify it each time.
+
+Hfsplus tools let you see the file and copy data from the HFS+
+partition but not to copy data to it or change the filenames or such like.
+
+HFS+ actually keeps two files for every one you see (called forks), a
+resource fork and a data fork. The default copy mode when you're
+copying files across to you native drive is raw data which means it
+just copies the data. Text files are copied in text mode (same as raw
+format but changes the line endings to be Unix friendly and gets rid
+of some funny extra characters - strongly advised for text files)
+unless you specify otherwise. You can also copy the files across in
+Mac Binary II format or specify text or raw format with another query:
+mac:/myfile?mode=b or mac:/myfile?mode=t See man hpcopy for more.
+
+Note that you need permissions to read your HFS+ partition. How you
+get this depends on your distribution, do a ls -l /dev/hdaX on it to
+see. Under Debian you have to be in the disk group (just add your
+username to the end of the entry in /etc/group).
+
+File types are done with matching the HFS+ type and application label
+and then by extentions. See the source for the exact matching that
+happens and feel free to suggest improvements.
+
+For some reason some directories in MacOS end in a funny tall f
+character. This seems to confuse hfstools.
+
+You can't easiily use the command line tools while you are browsing
+using kio-mac in Konqueror. Konqueror continuously refreshes it's
+view which mean hpmount is being called every few seconds. Click on
+Konqueror's home button before using the tools yourself on the command
+line.
+
+Hidden files are now shown all the time. Apparantly Konqueror only
+considers files with a dot at the front of the name to be hidden which
+is a bit system dependant.
+
+Please e-mail me with any comments, problems and success stories:
+Jonathan Riddell, jr@jriddell.org
diff --git a/kioslave/mac/TODO b/kioslave/mac/TODO
new file mode 100644
index 000000000..e94d88254
--- /dev/null
+++ b/kioslave/mac/TODO
@@ -0,0 +1,14 @@
+FIXMEs:
+ Amazingly, none that I can think of
+
+grep TODO kio_mac.cpp
+ //TODO this means dev=foo must be the last argument in the query
+ //TODO this error interrupts the user when typing ?dev=foo on each letter of foo
+ //TODO are there any more characters to escape?
+ QString theSize(fileRE.group(4)); //TODO: this is data size, what about resource size?
+
+Future things:
+ - maybe make it work with plain old hfs partitions
+ - possibly use libhfsp directly
+ - A Friend suggested reading the resource data for the icon to display - advanced I think.
+ - Follow symlinks/aliases (requires reading the resource fork as well)
diff --git a/kioslave/mac/cr16-app-mac.png b/kioslave/mac/cr16-app-mac.png
new file mode 100644
index 000000000..930694eab
--- /dev/null
+++ b/kioslave/mac/cr16-app-mac.png
Binary files differ
diff --git a/kioslave/mac/cr32-app-mac.png b/kioslave/mac/cr32-app-mac.png
new file mode 100644
index 000000000..87df6fb85
--- /dev/null
+++ b/kioslave/mac/cr32-app-mac.png
Binary files differ
diff --git a/kioslave/mac/kio_mac.cpp b/kioslave/mac/kio_mac.cpp
new file mode 100644
index 000000000..56989487a
--- /dev/null
+++ b/kioslave/mac/kio_mac.cpp
@@ -0,0 +1,561 @@
+/***************************************************************************
+ kio_mac.cpp
+ -------------------
+ copyright : (C) 2002 Jonathan Riddell
+ email : jr@jriddell.org
+ version : 1.0.1
+ release date : 19 July 2002
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#define PARTITION "/dev/hda11"
+
+#include <kinstance.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <qstring.h>
+#include <qregexp.h>
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <iostream>
+#include <time.h>
+
+#include "kio_mac.moc"
+
+using namespace KIO;
+
+extern "C" {
+ int KDE_EXPORT kdemain(int, char **argv) {
+ KInstance instance("kio_mac");
+ MacProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+ return 0;
+ }
+}
+
+MacProtocol::MacProtocol(const QCString &pool, const QCString &app)
+ : QObject(), SlaveBase("mac", pool, app) {
+/* logFile = new QFile("/home/jr/logfile");
+ logFile->open(IO_ReadWrite | IO_Append);
+ logStream = new QTextStream(logFile);
+ *logStream << "Start Macprotocol()" << endl;
+ */
+}
+
+MacProtocol::~MacProtocol() {
+/* *logStream << "destructor ~MacProtocol()" << endl;
+ logFile->close();
+ delete logFile;
+ logFile = 0;
+ delete logStream;
+ logStream = 0;
+*/
+ delete myKProcess;
+ myKProcess = 0L;
+}
+
+//get() called when a file is to be read
+void MacProtocol::get(const KURL& url) {
+ QString path = prepareHP(url); //mount and change to correct directory - return the filename
+ QString query = url.query();
+ QString mode("-");
+ QString mime;
+ processedBytes = 0;
+
+ //Find out the size and if it's a text file
+ UDSEntry entry = doStat(url);
+ UDSEntry::Iterator it;
+ for(it = entry.begin(); it != entry.end(); ++it) {
+ if ((*it).m_uds == KIO::UDS_MIME_TYPE) {
+ mime = (*it).m_str;
+ }
+ if ((*it).m_uds == KIO::UDS_SIZE) {
+ totalSize((*it).m_long);
+ }
+ }
+
+ //find out if a mode has been specified in the query e.g. ?mode=t
+ //or if it's a text file then set the mode to text
+ int modepos = query.find("mode=");
+ int textpos = mime.find("text");
+ if (modepos != -1) {
+ mode += query.mid(modepos + 5, 1);
+ if (mode != "-r" && mode != "-b" && mode != "-m" && mode != "-t" && mode != "-a") {
+ error(ERR_SLAVE_DEFINED, i18n("Unknown mode"));
+ }
+ } else if (textpos != -1) {
+ mode += "t";
+ } else {
+ mode += "r";
+ }
+
+ //now we can read the file
+ myKProcess = new KProcess();
+
+ *myKProcess << "hpcopy" << mode << path << "-";
+
+ //data is now sent directly from the slot
+ connect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotSetDataStdOutput(KProcess *, char *, int)));
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ if (!myKProcess->normalExit() || !(myKProcess->exitStatus() == 0)) {
+ error(ERR_SLAVE_DEFINED,
+ i18n("There was an error with hpcopy - please ensure it is installed"));
+ return;
+ }
+
+ //clean up
+ delete myKProcess; myKProcess = 0;
+ //finish
+ data(QByteArray());
+ finished();
+}
+
+//listDir() called when the user is looking at a directory
+void MacProtocol::listDir(const KURL& url) {
+ QString filename = prepareHP(url);
+
+ if (filename.isNull()) {
+ error(ERR_CANNOT_LAUNCH_PROCESS, i18n("No filename was found"));
+ } else {
+ myKProcess = new KProcess();
+ *myKProcess << "hpls" << "-la" << filename;
+
+ standardOutputStream = QString::null;
+ connect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
+ error(ERR_SLAVE_DEFINED,
+ i18n("There was an error with hpls - please ensure it is installed"));
+ }
+
+ //clean up
+ delete myKProcess; myKProcess = 0;
+ disconnect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ UDSEntry entry;
+ if (!standardOutputStream.isEmpty()) {
+ QTextStream in(&standardOutputStream, IO_ReadOnly);
+ QString line = in.readLine(); //throw away top file which shows current directory
+ line = in.readLine();
+
+ while (line != NULL) {
+ //1.0.4 puts this funny line in sometimes, we don't want it
+ if (line.contains("Thread ") == 0) {
+ entry = makeUDS(line);
+ listEntry(entry, false);
+ }
+ line = in.readLine();
+ }
+ }//if standardOutputStream != null
+
+ listEntry(entry, true);
+ finished();
+
+ }//if filename == null
+}
+
+//stat() called to see if it's a file or directory, called before listDir() or get()
+void MacProtocol::stat(const KURL& url) {
+ statEntry(doStat(url));
+ finished();
+}
+
+//doStat(), does all the work that stat() needs
+//it's been separated out so it can be called from get() which
+//also need information
+QValueList<KIO::UDSAtom> MacProtocol::doStat(const KURL& url) {
+ QString filename = prepareHP(url);
+
+ if (filename.isNull()) {
+ error(ERR_SLAVE_DEFINED, i18n("No filename was found in the URL"));
+ } else if (! filename.isEmpty()) {
+ myKProcess = new KShellProcess();
+
+ *myKProcess << "hpls" << "-ld" << filename;
+
+ standardOutputStream = QString::null;
+ connect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
+ error(ERR_SLAVE_DEFINED,
+ i18n("hpls did not exit normally - please ensure you have installed the hfsplus tools"));
+ }
+
+ //clean up
+ delete myKProcess; myKProcess = 0;
+ disconnect(myKProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ if (standardOutputStream.isEmpty()) {
+ filename.replace("\\ ", " "); //get rid of escapes
+ filename.replace("\\&", "&"); //mm, slashes...
+ filename.replace("\\!", "!");
+ filename.replace("\\(", "(");
+ filename.replace("\\)", ")");
+ error(ERR_DOES_NOT_EXIST, filename);
+ } else {
+ //remove trailing \n
+ QString line = standardOutputStream.left(standardOutputStream.length()-1);
+ UDSEntry entry = makeUDS(line);
+ return entry;
+ }
+ } else { //filename is empty means we're looking at root dir
+ //we don't have a listing for the root directory so here's a dummy one
+ UDSEntry entry = makeUDS("d 0 item Jan 01 2000 /");
+ return entry;
+ }//if filename == null
+
+ return QValueList<KIO::UDSAtom>();
+}
+
+//prepareHP() called from get() listDir() and stat()
+//(re)mounts the partition and changes to the appropriate directory
+QString MacProtocol::prepareHP(const KURL& url) {
+ QString path = url.path(-1);
+ if (path.left(1) == "/") {
+ path = path.mid(1); // strip leading slash
+ }
+
+ //find out if a device has been specified in the query e.g. ?dev=/dev/fd0
+ //or in the config file (query device entries are saved to config file)
+ QString device;
+ KConfig* config = new KConfig("macrc");
+
+ QString query = url.query();
+ int modepos = query.find("dev=");
+ if (modepos == -1) {
+ //no device specified, read from config or go with #define PARTITION
+ device = config->readEntry("device",PARTITION);
+ } else {
+ //TODO this means dev=foo must be the last argument in the query
+ device = query.mid(modepos + 4);
+ config->writeEntry("device",device);
+ }
+ delete config; config = 0;
+
+ //first we run just hpmount and check the output to see if it's version 1.0.2 or 1.0.4
+ myKProcess = new KProcess();
+ *myKProcess << "hpmount";
+ standardOutputStream = QString::null;
+ connect(myKProcess, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ bool version102 = true;
+
+ if (standardOutputStream.contains("options") != 0) {
+ version102 = false;
+ }
+
+ delete myKProcess; myKProcess = 0;
+ disconnect(myKProcess, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+
+ //now mount the drive
+ myKProcess = new KProcess();
+ if (version102) {
+ *myKProcess << "hpmount" << device;
+ } else {
+ *myKProcess << "hpmount" << "-r" << device;
+ }
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
+ //TODO this error interrupts the user when typing ?dev=foo on each letter of foo
+ error(ERR_SLAVE_DEFINED,
+ i18n("hpmount did not exit normally - please ensure that hfsplus utils are installed,\n"
+ "that you have permission to read the partition (ls -l /dev/hdaX)\n"
+ "and that you have specified the correct partition.\n"
+ "You can specify partitions by adding ?dev=/dev/hda2 to the URL."));
+ return NULL;
+ }
+
+ //clean up
+ delete myKProcess; myKProcess = 0;
+
+ //escape any funny characters
+ //TODO are there any more characters to escape?
+ path.replace(" ", "\\ ");
+ path.replace("&", "\\&");
+ path.replace("!", "\\!");
+ path.replace("(", "\\(");
+ path.replace(")", "\\)");
+
+ //then change to the right directory
+ int s; QString dir;
+ s = path.find('/');
+ while (s != -1) {
+ dir = path.left(s);
+ path = path.mid(s+1);
+
+ myKProcess = new KProcess();
+ *myKProcess << "hpcd" << dir;
+
+ myKProcess->start(KProcess::Block, KProcess::All);
+
+ if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
+ error(ERR_SLAVE_DEFINED,
+ i18n("hpcd did not exit normally - please ensure it is installed"));
+ return NULL;
+ }
+
+ //clean up
+ delete myKProcess; myKProcess = 0;
+
+ s = path.find('/');
+ }
+
+ return path;
+}
+
+//makeUDS() takes a line of output from hpls -l and converts it into
+// one of these UDSEntrys to return
+//called from listDir() and stat()
+QValueList<KIO::UDSAtom> MacProtocol::makeUDS(const QString& _line) {
+ QString line(_line);
+ UDSEntry entry;
+
+ //is it a file or a directory
+ QRegExp dirRE("^d. +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +(.*)");
+ QRegExp fileRE("^([f|F]). +(....)/(....) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +(.*)");
+ if (dirRE.exactMatch(line)) {
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = dirRE.cap(6);
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MODIFICATION_TIME;
+ atom.m_long = makeTime(dirRE.cap(4), dirRE.cap(3), dirRE.cap(5));
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = 0755;
+ entry.append(atom);
+
+ } else if (fileRE.exactMatch(line)) {
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = fileRE.cap(9);
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_SIZE;
+ QString theSize(fileRE.cap(4)); //TODO: this is data size, what about resource size?
+ atom.m_long = theSize.toLong();
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MODIFICATION_TIME;
+ atom.m_long = makeTime(fileRE.cap(7), fileRE.cap(6), fileRE.cap(8));
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ if (QString(fileRE.cap(1)) == QString("F")) { //if locked then read only
+ atom.m_long = 0444;
+ } else {
+ atom.m_long = 0644;
+ }
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ QString mimetype = getMimetype(fileRE.cap(2),fileRE.cap(3));
+ atom.m_str = mimetype.local8Bit();
+ entry.append(atom);
+
+ // Is it a file or a link/alias, just make aliases link to themselves
+ if (QString(fileRE.cap(2)) == QString("adrp") ||
+ QString(fileRE.cap(2)) == QString("fdrp")) {
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_LINK_DEST;
+ atom.m_str = fileRE.cap(9); //I have a file called "Mozilla alias" the name
+ // of which displays funny because of this.
+ // No idea why. Same for other kioslaves. A font thing?
+ entry.append(atom);
+ } else {
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+ }
+ } else {
+ error(ERR_INTERNAL, i18n("hpls output was not matched"));
+ } //if match dirRE or fileRE
+
+ return entry;
+}
+
+//slotGetStdOutput() grabs output from the hp commands
+// and adds it to the buffer
+void MacProtocol::slotGetStdOutput(KProcess*, char *s, int len) {
+ standardOutputStream += QString::fromLocal8Bit(s, len);
+}
+
+//slotSetDataStdOutput() is used during hpcopy to give
+//standard output to KDE
+void MacProtocol::slotSetDataStdOutput(KProcess*, char *s, int len) {
+ processedBytes += len;
+ processedSize(processedBytes);
+ QByteArray array;
+ array.setRawData(s, len);
+ data(array);
+ array.resetRawData(s, len);
+}
+
+//makeTime() takes in the date output from hpls -l
+//and returns as good a timestamp as we're going to get
+int MacProtocol::makeTime(QString mday, QString mon, QString third) {
+ int year; int month; int day;
+ int hour; int minute;
+
+ //find the month
+ if (mon == "Jan") { month = 1; }
+ else if (mon == "Feb") { month = 2; }
+ else if (mon == "Mar") { month = 3; }
+ else if (mon == "Apr") { month = 4; }
+ else if (mon == "May") { month = 5; }
+ else if (mon == "Jun") { month = 6; }
+ else if (mon == "Jul") { month = 7; }
+ else if (mon == "Aug") { month = 8; }
+ else if (mon == "Sep") { month = 9; }
+ else if (mon == "Oct") { month = 10; }
+ else if (mon == "Nov") { month = 11; }
+ else if (mon == "Dec") { month = 12; }
+ else {
+ error(ERR_INTERNAL, i18n("Month output from hpls -l not matched"));
+ month = 13;
+ }
+
+ //if the file is recent (last 12 months) hpls gives us the time,
+ // otherwise it only prints the year
+ QRegExp hourMin("(..):(..)");
+ if (hourMin.exactMatch(third)) {
+ QDate currentDate(QDate::currentDate());
+
+ if (month > currentDate.month()) {
+ year = currentDate.year() - 1;
+ } else {
+ year = currentDate.year();
+ }
+ QString h(hourMin.cap(1));
+ QString m(hourMin.cap(2));
+ hour = h.toInt();
+ minute = m.toInt();
+ } else {
+ year = third.toInt();
+ hour = 0;
+ minute = 0;
+ }// if hour:min or year
+
+ day = mday.toInt();
+
+ //check it's valid
+ if ( (!QDate::isValid(year, month, day)) || (!QTime::isValid(hour, minute, 0) ) ) {
+ error(ERR_INTERNAL, i18n("Could not parse a valid date from hpls"));
+ }
+
+ //put it together and work it out
+ QDate fileDate(year, month, day);
+ QTime fileTime(hour, minute);
+ QDateTime fileDateTime(fileDate, fileTime);
+
+ return fileDateTime.toTime_t();
+}
+
+QString MacProtocol::getMimetype(QString type, QString app) {
+ if (type == QString("TEXT") && app == QString("ttxt")) {
+ return QString("text/plain");
+ } else if (type == QString("TEXT") && app == QString("udog")) {
+ return QString("text/html");
+ } else if (type == QString("svgs")) {
+ return QString("text/xml");
+ } else if (type == QString("ZIP ")) {
+ return QString("application/zip");
+ } else if (type == QString("pZip")) {
+ return QString("application/zip");
+ } else if (type == QString("APPL")) {
+ return QString("application/x-executable");
+ } else if (type == QString("MooV")) {
+ return QString("video/quicktime");
+ } else if (type == QString("TEXT") && app == QString("MSWD")) {
+ return QString("application/vnd.ms-word");
+ } else if (type == QString("PDF ")) {
+ return QString("application/pdf");
+ } else if (app == QString("CARO")) {
+ return QString("application/pdf");
+ } else if (type == QString("SIT5")) {
+ return QString("application/x-stuffit");
+ } else if (type == QString("SITD")) {
+ return QString("application/x-stuffit");
+ } else if (type == QString("SIT!")) {
+ return QString("application/x-stuffit");
+ } else if (app == QString("SIT!")) {
+ return QString("application/x-stuffit");
+ } else if (type == QString("RTFf")) {
+ return QString("text/rtf");
+ } else if (type == QString("GIFf")) {
+ return QString("image/gif");
+ } else if (type == QString("JPEG")) {
+ return QString("image/jpeg");
+ } else if (type == QString("PNGf")) {
+ return QString("image/png");
+ } else if (type == QString("XBMm")) {
+ return QString("image/x-xbm");
+ } else if (type == QString("EPSF")) {
+ return QString("image/x-epsf");
+ } else if (type == QString("TIFF")) {
+ return QString("image/tiff");
+ } else if (type == QString("PICT")) {
+ return QString("image/pict");
+ } else if (type == QString("TPIC")) {
+ return QString("image/x-targa");
+ } else if (type == QString("ULAW")) {
+ return QString("audio/basic");
+ } else if (type == QString("AIFF")) {
+ return QString("audio/x-aiff");
+ } else if (type == QString("WAVE")) {
+ return QString("audio/x-wav");
+ } else if (type == QString("FFIL") && app == QString("DMOV")) {
+ return QString("application/x-font");
+ } else if (type == QString("XLS3")) {
+ return QString("application/vnd.ms-excel");
+ } else if (type == QString("XLS4")) {
+ return QString("application/vnd.ms-excel");
+ } else if (type == QString("XLS5")) {
+ return QString("application/vnd.ms-excel");
+ } else if (app == QString("MSWD")) {
+ return QString("application/vnd.ms-word");
+ } else if (type == QString("TEXT")) {
+ return QString("text/plain");
+ } else if (app == QString("ttxt")) {
+ return QString("text/plain");
+ }
+ return QString("application/octet-stream");
+}
+
+
diff --git a/kioslave/mac/kio_mac.h b/kioslave/mac/kio_mac.h
new file mode 100644
index 000000000..c87217e08
--- /dev/null
+++ b/kioslave/mac/kio_mac.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ mac.cpp
+ -------------------
+ copyright : (C) 2002 Jonathan Riddell
+ email : jr@jriddell.org
+ version : 1.0
+ release date : 10 Feburary 2002
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kio/slavebase.h>
+#include <kio/global.h>
+#include <kurl.h>
+#include <kprocess.h>
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+class MacProtocol : public QObject, public KIO::SlaveBase
+{
+ Q_OBJECT
+public:
+ MacProtocol(const QCString &pool, const QCString &app);
+ ~MacProtocol();
+ virtual void get(const KURL& url );
+ virtual void listDir(const KURL& url);
+ virtual void stat(const KURL& url);
+protected slots:
+ void slotGetStdOutput(KProcess*, char*, int);
+ void slotSetDataStdOutput(KProcess*, char *s, int len);
+protected:
+ QString prepareHP(const KURL& _url);
+ QValueList<KIO::UDSAtom> makeUDS(const QString& _line);
+ int makeTime(QString mday, QString mon, QString third);
+ QString getMimetype(QString type, QString app);
+ QValueList<KIO::UDSAtom> doStat(const KURL& url);
+
+ KIO::filesize_t processedBytes;
+ QString standardOutputStream;
+ KProcess* myKProcess;
+
+ //for debugging
+ //QFile* logFile;
+ //QTextStream* logStream;
+};
diff --git a/kioslave/mac/mac.protocol b/kioslave/mac/mac.protocol
new file mode 100644
index 000000000..cef77621d
--- /dev/null
+++ b/kioslave/mac/mac.protocol
@@ -0,0 +1,73 @@
+[Protocol]
+exec=kio_mac
+protocol=mac
+input=none
+output=filesystem
+reading=true
+listing=Name,Type,Size,Date
+defaultMimetype=application/octet-stream
+Description=A kioslave for MacOS HFS+ partitions
+Description[af]='n Kioslave vir MacOS HFS+ partisies
+Description[be]=Kioslave для раздзелаў MacOS HFS+
+Description[bn]=ম্যাক-ও-এস HFS+ পার্টিশন-এর জন্য একটি kioslave
+Description[br]=Ur c'hioslave evit ar parzhadurioù MacOS HFS+
+Description[bs]=kioslave za MacOS HFS+ particije
+Description[ca]=Un kioslave per a particions MacOS HFS+
+Description[cs]=Pomocný protokol pro diskové oddíly MacOS HFS+
+Description[csb]=Plugins protokòłu dlô particëji HFS+ systemë MacOS
+Description[da]=En kioslave for MacOS HFS+ partitioner
+Description[de]=Ein-/Ausgabemodul für MacOS HFS+ Partitionen
+Description[el]=Ένας kioslave για κατατμήσεις MacOS HFS+
+Description[eo]=K-enel-sklavo por MacOS HFS+ subdiskoj
+Description[es]=Un kioslave para particiones MacOS HFS+
+Description[et]=MacOS-i HFS+-partitsioonide IO-moodul
+Description[eu]=MacOS HFS+ zatiketetarako kioslavea
+Description[fa]=یک kioslave برای افرازهای HFS+ سیستم عامل مکینتاش
+Description[fi]=Liitäntä MacOS HFS+ osioinneille
+Description[fr]=Un module d'entrées / sorties pour les partitions MacOS HFS+
+Description[fy]=In kioslave foar MacOS HFS+-partities
+Description[ga]=kioslave le haghaidh deighiltí MacOS HFS+
+Description[gl]=Un kioslave para particións MacOS HFS+
+Description[he]=ממשק kioslave עבור מחיצות MacOS HFS+
+Description[hi]=मॅक-ओएस एचएफ़एस+ पार्टीशनों के लिए के-आई-ओ-स्लेव
+Description[hr]=Kioslave za MacOS HFS+ particije
+Description[hu]=KDE-protokoll MacOS HFS+ partíciók kezeléséhez
+Description[is]=kioslave fyrir MacOS HFS+ disksneiðar
+Description[it]=Un kioslave per partizioni MacOS HFS+
+Description[ja]=MacOS HFS+ パーティションのための kioslave
+Description[ka]=kioslave MacOS HFS+ პარტიციებისთვის
+Description[kk]=MacOS HFS+ файл жүйесінің енгізу-шығару модулі
+Description[km]=kioslave សម្រាប់​ភាគ MacOS HFS+
+Description[lt]=Kiovergas MacOS HFS+ dalmenims
+Description[lv]=KIO vergs MacOS HFS+ partīcijām
+Description[mk]=kio-служител за HFS+ партиции од MacOS
+Description[ms]=Hamba kio untuk MacOS HFS+ petak
+Description[nb]=En kioskslave for MacOS HFS+-partisjoner
+Description[nds]=En In-/Utgaavdeenst för MacOS-HFS+-Partitschonen
+Description[ne]=MacOS HFS+ विभाजनका लागि किओस्लेभ
+Description[nl]=Een kioslave voor MacOS HFS+-partities
+Description[nn]=Ein IU-slave for MacOS HFS+-partisjonar
+Description[pa]=MacOS HFS+ ਭਾਗਾਂ ਲਈ kioslave
+Description[pl]=Wtyczka protokołu dla partycji HFS+ systemu MacOS
+Description[pt]=Um 'kioslave' para partições MacOS HFS+
+Description[pt_BR]=Um protocolo para as partições HFS+ do MacOS
+Description[ro]=Un dispozitiv de I/E pentru partiții HFS+ MacOS
+Description[ru]=Модуль ввода-вывода для файловой системы MacOS HFS+
+Description[rw]=Kio-umugaragu ya MacOS HFS+ibicedisiki
+Description[se]=SO-šláva MacOS HFS+-partišuvnnaid várás
+Description[sk]=kioslave pre MacOS HFS+
+Description[sl]=kioslave za razdelke MacOS HFS+
+Description[sr]=Kioslave за MacOS-ове HFS+ партиције
+Description[sr@Latn]=Kioslave za MacOS-ove HFS+ particije
+Description[sv]=En I/O-slav för MacOS HFS+ partitioner
+Description[ta]=MacOS HFS+ partitionsக்கு ஒரு க்யோஸ்லேவ்
+Description[th]=ตัวนำข้อมูลเข้า-ออกสำหรับพาร์ติชั่นที่ใช้ระบบไฟล์ HFS+ ของ MacOS
+Description[tr]=MacOS HFS+ bölümleri için kioslave
+Description[tt]=MacOS HFS+ bülemnäre öçen birem sistemeneñ modulı
+Description[uk]=Підлеглий B/В для розділів MacOS HFS+
+Description[vi]=A kioslave (đày tớ vào ra KDE) cho MacOS HFS và các phân vùng
+Description[wa]=On kioslave po MacOS HFS + pårticions
+Description[zh_CN]=MacOS HFS+ 分区的 KIO 仆人
+Description[zh_TW]=用於 MacOS HFS+ 分割區的 kioslave
+Icon=mac
+DocPath=kioslave/mac.html
diff --git a/kioslave/man/LICENSE b/kioslave/man/LICENSE
new file mode 100644
index 000000000..d28a48f92
--- /dev/null
+++ b/kioslave/man/LICENSE
@@ -0,0 +1,16 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/kioslave/man/Makefile.am b/kioslave/man/Makefile.am
new file mode 100644
index 000000000..365d2f774
--- /dev/null
+++ b/kioslave/man/Makefile.am
@@ -0,0 +1,51 @@
+## Makefile.am of kdebase/kioslave/man
+
+INCLUDES= $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+EXTRA_PROGRAMS = kio_man_test man2html
+
+####### just for testing (j.habenicht@europemail.com, 15.02.2001)
+
+kio_man_test_SOURCES = kio_man_test.cpp
+kio_man_test_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kio_man_test_LDADD = man2html.lo kio_man.lo $(LIB_KIO) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_man.la libkmanpart.la
+
+kio_man_la_SOURCES = man2html.cpp kio_man.cpp
+kio_man_la_LIBADD = $(LIB_KSYCOCA)
+kio_man_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+noinst_HEADERS = kio_man.h
+### TODO Why is man2htmk.h distributed?
+
+libkmanpart_la_SOURCES = kmanpart.cpp
+libkmanpart_la_LIBADD = -lkhtml $(LIB_KPARTS)
+libkmanpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN)
+
+kdelnk_DATA = man.protocol kmanpart.desktop
+kdelnkdir = $(kde_servicesdir)
+
+kio_man_data_DATA = kio_man.css
+kio_man_datadir = $(kde_datadir)/kio_man
+EXTRA_DIST=$(kio_man_data_DATA)
+
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kio_man.pot
+
+man2html_SOURCES = dummy.cpp
+man2html_LDADD = man2html_simple.o $(LIB_QT)
+man2html_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+dummy.cpp:
+ echo > $@
+
+man2html_simple.o: $(srcdir)/man2html.cpp
+ -rm -f man2html_simple.cpp
+ $(LN_S) $(srcdir)/man2html.cpp man2html_simple.cpp
+ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) -DSIMPLE_MAN2HTML $(INCLUDES) $(CPPFLAGS) $(CXXFLAGS) -c man2html_simple.cpp
+
diff --git a/kioslave/man/kio_man.cpp b/kioslave/man/kio_man.cpp
new file mode 100644
index 000000000..0511a165d
--- /dev/null
+++ b/kioslave/man/kio_man.cpp
@@ -0,0 +1,1532 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2000 Matthias Hoelzer-Kluepfel <mhk@caldera.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <dirent.h>
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+#include <qcstring.h>
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kmimetype.h>
+
+#include "kio_man.h"
+#include "kio_man.moc"
+#include "man2html.h"
+#include <assert.h>
+#include <kfilterbase.h>
+#include <kfilterdev.h>
+
+using namespace KIO;
+
+MANProtocol *MANProtocol::_self = 0;
+
+#define SGML2ROFF_DIRS "/usr/lib/sgml"
+
+/*
+ * Drop trailing ".section[.gz]" from name
+ */
+static
+void stripExtension( QString *name )
+{
+ int pos = name->length();
+
+ if ( name->find(".gz", -3) != -1 )
+ pos -= 3;
+ else if ( name->find(".z", -2, false) != -1 )
+ pos -= 2;
+ else if ( name->find(".bz2", -4) != -1 )
+ pos -= 4;
+ else if ( name->find(".bz", -3) != -1 )
+ pos -= 3;
+
+ if ( pos > 0 )
+ pos = name->findRev('.', pos-1);
+
+ if ( pos > 0 )
+ name->truncate( pos );
+}
+
+static
+bool parseUrl(const QString& _url, QString &title, QString &section)
+{
+ section = QString::null;
+
+ QString url = _url;
+ if (url.at(0) == '/') {
+ if (KStandardDirs::exists(url)) {
+ title = url;
+ return true;
+ } else
+ {
+ // If the directory does not exist, then it is perhaps a normal man page
+ kdDebug(7107) << url << " does not exist" << endl;
+ }
+ }
+
+ while (url.at(0) == '/')
+ url.remove(0,1);
+
+ title = url;
+
+ int pos = url.find('(');
+ if (pos < 0)
+ return true;
+
+ title = title.left(pos);
+
+ section = url.mid(pos+1);
+ section = section.left(section.length()-1);
+
+ return true;
+}
+
+
+MANProtocol::MANProtocol(const QCString &pool_socket, const QCString &app_socket)
+ : QObject(), SlaveBase("man", pool_socket, app_socket)
+{
+ assert(!_self);
+ _self = this;
+ const QString common_dir = KGlobal::dirs()->findResourceDir( "html", "en/common/kde-common.css" );
+ const QString strPath=QString( "file:%1/en/common" ).arg( common_dir );
+ m_htmlPath=strPath.local8Bit(); // ### TODO encode for HTML
+ m_cssPath=strPath.local8Bit(); // ### TODO encode for CSS
+ section_names << "1" << "2" << "3" << "3n" << "3p" << "4" << "5" << "6" << "7"
+ << "8" << "9" << "l" << "n";
+ m_manCSSFile = locate( "data", "kio_man/kio_man.css" );
+}
+
+MANProtocol *MANProtocol::self() { return _self; }
+
+MANProtocol::~MANProtocol()
+{
+ _self = 0;
+}
+
+void MANProtocol::parseWhatIs( QMap<QString, QString> &i, QTextStream &t, const QString &mark )
+{
+ QRegExp re( mark );
+ QString l;
+ while ( !t.atEnd() )
+ {
+ l = t.readLine();
+ int pos = re.search( l );
+ if (pos != -1)
+ {
+ QString names = l.left(pos);
+ QString descr = l.mid(pos + re.matchedLength());
+ while ((pos = names.find(",")) != -1)
+ {
+ i[names.left(pos++)] = descr;
+ while (names[pos] == ' ')
+ pos++;
+ names = names.mid(pos);
+ }
+ i[names] = descr;
+ }
+ }
+}
+
+bool MANProtocol::addWhatIs(QMap<QString, QString> &i, const QString &name, const QString &mark)
+{
+ QFile f(name);
+ if (!f.open(IO_ReadOnly))
+ return false;
+ QTextStream t(&f);
+ parseWhatIs( i, t, mark );
+ return true;
+}
+
+QMap<QString, QString> MANProtocol::buildIndexMap(const QString &section)
+{
+ QMap<QString, QString> i;
+ QStringList man_dirs = manDirectories();
+ // Supplementary places for whatis databases
+ man_dirs += m_mandbpath;
+ if (man_dirs.find("/var/cache/man")==man_dirs.end())
+ man_dirs << "/var/cache/man";
+ if (man_dirs.find("/var/catman")==man_dirs.end())
+ man_dirs << "/var/catman";
+
+ QStringList names;
+ names << "whatis.db" << "whatis";
+ QString mark = "\\s+\\(" + section + "[a-z]*\\)\\s+-\\s+";
+
+ for ( QStringList::ConstIterator it_dir = man_dirs.begin();
+ it_dir != man_dirs.end();
+ ++it_dir )
+ {
+ if ( QFile::exists( *it_dir ) ) {
+ QStringList::ConstIterator it_name;
+ for ( it_name = names.begin();
+ it_name != names.end();
+ it_name++ )
+ {
+ if (addWhatIs(i, (*it_dir) + "/" + (*it_name), mark))
+ break;
+ }
+ if ( it_name == names.end() ) {
+ KProcess proc;
+ proc << "whatis" << "-M" << (*it_dir) << "-w" << "*";
+ myStdStream = QString::null;
+ connect( &proc, SIGNAL( receivedStdout(KProcess *, char *, int ) ),
+ SLOT( slotGetStdOutput( KProcess *, char *, int ) ) );
+ proc.start( KProcess::Block, KProcess::Stdout );
+ QTextStream t( &myStdStream, IO_ReadOnly );
+ parseWhatIs( i, t, mark );
+ }
+ }
+ }
+ return i;
+}
+
+QStringList MANProtocol::manDirectories()
+{
+ checkManPaths();
+ //
+ // Build a list of man directories including translations
+ //
+ QStringList man_dirs;
+
+ for ( QStringList::ConstIterator it_dir = m_manpath.begin();
+ it_dir != m_manpath.end();
+ it_dir++ )
+ {
+ // Translated pages in "<mandir>/<lang>" if the directory
+ // exists
+ QStringList languages = KGlobal::locale()->languageList();
+
+ for (QStringList::ConstIterator it_lang = languages.begin();
+ it_lang != languages.end();
+ it_lang++ )
+ {
+ if ( !(*it_lang).isEmpty() && (*it_lang) != QString("C") ) {
+ QString dir = (*it_dir) + '/' + (*it_lang);
+
+ struct stat sbuf;
+
+ if ( ::stat( QFile::encodeName( dir ), &sbuf ) == 0
+ && S_ISDIR( sbuf.st_mode ) )
+ {
+ const QString p = QDir(dir).canonicalPath();
+ if (!man_dirs.contains(p)) man_dirs += p;
+ }
+ }
+ }
+
+ // Untranslated pages in "<mandir>"
+ const QString p = QDir(*it_dir).canonicalPath();
+ if (!man_dirs.contains(p)) man_dirs += p;
+ }
+ return man_dirs;
+}
+
+QStringList MANProtocol::findPages(const QString &_section,
+ const QString &title,
+ bool full_path)
+{
+ QString section = _section;
+
+ QStringList list;
+
+ // kdDebug() << "findPages '" << section << "' '" << title << "'\n";
+ if (title.at(0) == '/') {
+ list.append(title);
+ return list;
+ }
+
+ const QString star( "*" );
+
+ //
+ // Find man sections in this directory
+ //
+ QStringList sect_list;
+ if ( section.isEmpty() )
+ section = star;
+
+ if ( section != star )
+ {
+ //
+ // Section given as argument
+ //
+ sect_list += section;
+ while (section.at(section.length() - 1).isLetter()) {
+ section.truncate(section.length() - 1);
+ sect_list += section;
+ }
+ } else {
+ sect_list += section;
+ }
+
+ QStringList man_dirs = manDirectories();
+
+ //
+ // Find man pages in the sections listed above
+ //
+ for ( QStringList::ConstIterator it_sect = sect_list.begin();
+ it_sect != sect_list.end();
+ it_sect++ )
+ {
+ QString it_real = (*it_sect).lower();
+ //
+ // Find pages
+ //
+ for ( QStringList::ConstIterator it_dir = man_dirs.begin();
+ it_dir != man_dirs.end();
+ it_dir++ )
+ {
+ QString man_dir = (*it_dir);
+
+ //
+ // Sections = all sub directories "man*" and "sman*"
+ //
+ DIR *dp = ::opendir( QFile::encodeName( man_dir ) );
+
+ if ( !dp )
+ continue;
+
+ struct dirent *ep;
+
+ const QString man = QString("man");
+ const QString sman = QString("sman");
+
+ while ( (ep = ::readdir( dp )) != 0L ) {
+ const QString file = QFile::decodeName( ep->d_name );
+ QString sect = QString::null;
+
+ if ( file.startsWith( man ) )
+ sect = file.mid(3);
+ else if (file.startsWith(sman))
+ sect = file.mid(4);
+
+ if (sect.lower()==it_real) it_real = sect;
+
+ // Only add sect if not already contained, avoid duplicates
+ if (!sect_list.contains(sect) && _section.isEmpty()) {
+ kdDebug() << "another section " << sect << endl;
+ sect_list += sect;
+ }
+ }
+
+ ::closedir( dp );
+
+ if ( *it_sect != star ) { // in that case we only look around for sections
+ const QString dir = man_dir + QString("/man") + (it_real) + '/';
+ const QString sdir = man_dir + QString("/sman") + (it_real) + '/';
+
+ findManPagesInSection(dir, title, full_path, list);
+ findManPagesInSection(sdir, title, full_path, list);
+ }
+ }
+ }
+
+// kdDebug(7107) << "finished " << list << " " << sect_list << endl;
+
+ return list;
+}
+
+void MANProtocol::findManPagesInSection(const QString &dir, const QString &title, bool full_path, QStringList &list)
+{
+ kdDebug() << "findManPagesInSection " << dir << " " << title << endl;
+ bool title_given = !title.isEmpty();
+
+ DIR *dp = ::opendir( QFile::encodeName( dir ) );
+
+ if ( !dp )
+ return;
+
+ struct dirent *ep;
+
+ while ( (ep = ::readdir( dp )) != 0L ) {
+ if ( ep->d_name[0] != '.' ) {
+
+ QString name = QFile::decodeName( ep->d_name );
+
+ // check title if we're looking for a specific page
+ if ( title_given ) {
+ if ( !name.startsWith( title ) ) {
+ continue;
+ }
+ else {
+ // beginning matches, do a more thorough check...
+ QString tmp_name = name;
+ stripExtension( &tmp_name );
+ if ( tmp_name != title )
+ continue;
+ }
+ }
+
+ if ( full_path )
+ name.prepend( dir );
+
+ list += name ;
+ }
+ }
+ ::closedir( dp );
+}
+
+void MANProtocol::output(const char *insert)
+{
+ if (insert)
+ {
+ m_outputBuffer.writeBlock(insert,strlen(insert));
+ }
+ if (!insert || m_outputBuffer.at() >= 2048)
+ {
+ m_outputBuffer.close();
+ data(m_outputBuffer.buffer());
+ m_outputBuffer.setBuffer(QByteArray());
+ m_outputBuffer.open(IO_WriteOnly);
+ }
+}
+
+// called by man2html
+char *read_man_page(const char *filename)
+{
+ return MANProtocol::self()->readManPage(filename);
+}
+
+// called by man2html
+void output_real(const char *insert)
+{
+ MANProtocol::self()->output(insert);
+}
+
+static QString text2html(const QString& txt)
+{
+ QString reply = txt;
+
+ reply = reply.replace('&', "&amp;");
+ reply = reply.replace('<', "&lt;");
+ reply = reply.replace('>', "&gt;");
+ reply = reply.replace('"', "&dquot;");
+ reply = reply.replace('\'', "&quot;");
+ return reply;
+}
+
+void MANProtocol::get(const KURL& url )
+{
+ kdDebug(7107) << "GET " << url.url() << endl;
+
+ QString title, section;
+
+ if (!parseUrl(url.path(), title, section))
+ {
+ showMainIndex();
+ return;
+ }
+
+ // see if an index was requested
+ if (url.query().isEmpty() && (title.isEmpty() || title == "/" || title == "."))
+ {
+ if (section == "index" || section.isEmpty())
+ showMainIndex();
+ else
+ showIndex(section);
+ return;
+ }
+
+ // tell the mimetype
+ mimeType("text/html");
+
+ const QStringList foundPages=findPages(section, title);
+ bool pageFound=true;
+ if (foundPages.isEmpty())
+ {
+ outputError(i18n("No man page matching to %1 found.<br><br>"
+ "Check that you have not mistyped the name of the page that you want.\n"
+ "Be careful that you must take care about upper case and lower case characters!<br>"
+ "If everything looks correct, then perhaps you need to set a better search path "
+ "for man pages, be it by the environment variable MANPATH or a matching file "
+ "in the directory /etc .").arg(text2html(title)));
+ pageFound=false;
+ }
+ else if (foundPages.count()>1)
+ {
+ pageFound=false;
+ //check for the case that there is foo.1 and foo.1.gz found:
+ // ### TODO make it more generic (other extensions)
+ if ((foundPages.count()==2) &&
+ (((foundPages[0]+".gz") == foundPages[1]) ||
+ (foundPages[0] == (foundPages[1]+".gz"))))
+ pageFound=true;
+ else
+ outputMatchingPages(foundPages);
+ }
+ //yes, we found exactly one man page
+
+ if (pageFound)
+ {
+ setResourcePath(m_htmlPath,m_cssPath);
+ m_outputBuffer.open(IO_WriteOnly);
+ const QCString filename=QFile::encodeName(foundPages[0]);
+ char *buf = readManPage(filename);
+
+ if (!buf)
+ {
+ outputError(i18n("Open of %1 failed.").arg(title));
+ finished();
+ return;
+ }
+ // will call output_real
+ scan_man_page(buf);
+ delete [] buf;
+
+ output(0); // flush
+
+ m_outputBuffer.close();
+ data(m_outputBuffer.buffer());
+ m_outputBuffer.setBuffer(QByteArray());
+ // tell we are done
+ data(QByteArray());
+ }
+ finished();
+}
+
+void MANProtocol::slotGetStdOutput(KProcess* /* p */, char *s, int len)
+{
+ myStdStream += QString::fromLocal8Bit(s, len);
+}
+
+char *MANProtocol::readManPage(const char *_filename)
+{
+ QCString filename = _filename;
+
+ char *buf = NULL;
+
+ /* Determine type of man page file by checking its path. Determination by
+ * MIME type with KMimeType doesn't work reliablely. E.g., Solaris 7:
+ * /usr/man/sman7fs/pcfs.7fs -> text/x-csrc : WRONG
+ * If the path name constains the string sman, assume that it's SGML and
+ * convert it to roff format (used on Solaris). */
+ //QString file_mimetype = KMimeType::findByPath(QString(filename), 0, false)->name();
+ if (filename.contains("sman", false)) //file_mimetype == "text/html" || )
+ {
+ myStdStream =QString::null;
+ KProcess proc;
+
+ /* Determine path to sgml2roff, if not already done. */
+ getProgramPath();
+ proc << mySgml2RoffPath << filename;
+
+ QApplication::connect(&proc, SIGNAL(receivedStdout (KProcess *, char *, int)),
+ this, SLOT(slotGetStdOutput(KProcess *, char *, int)));
+ proc.start(KProcess::Block, KProcess::All);
+
+ const QCString cstr=myStdStream.latin1();
+ const int len = cstr.size()-1;
+ buf = new char[len + 4];
+ qmemmove(buf + 1, cstr.data(), len);
+ buf[0]=buf[len]='\n'; // Start and end with a end of line
+ buf[len+1]=buf[len+2]='\0'; // Two additional NUL characters at end
+ }
+ else
+ {
+ if (QDir::isRelativePath(filename)) {
+ kdDebug(7107) << "relative " << filename << endl;
+ filename = QDir::cleanDirPath(lastdir + "/" + filename).utf8();
+ if (!KStandardDirs::exists(filename)) { // exists perhaps with suffix
+ lastdir = filename.left(filename.findRev('/'));
+ QDir mandir(lastdir);
+ mandir.setNameFilter(filename.mid(filename.findRev('/') + 1) + ".*");
+ filename = lastdir + "/" + QFile::encodeName(mandir.entryList().first());
+ }
+ kdDebug(7107) << "resolved to " << filename << endl;
+ }
+ lastdir = filename.left(filename.findRev('/'));
+
+ QIODevice *fd= KFilterDev::deviceForFile(filename);
+
+ if ( !fd || !fd->open(IO_ReadOnly))
+ {
+ delete fd;
+ return 0;
+ }
+ QByteArray array(fd->readAll());
+ kdDebug(7107) << "read " << array.size() << endl;
+ fd->close();
+ delete fd;
+
+ if (array.isEmpty())
+ return 0;
+
+ const int len = array.size();
+ buf = new char[len + 4];
+ qmemmove(buf + 1, array.data(), len);
+ buf[0]=buf[len]='\n'; // Start and end with a end of line
+ buf[len+1]=buf[len+2]='\0'; // Two NUL characters at end
+ }
+ return buf;
+}
+
+
+void MANProtocol::outputError(const QString& errmsg)
+{
+ QByteArray array;
+ QTextStream os(array, IO_WriteOnly);
+ os.setEncoding(QTextStream::UnicodeUTF8);
+
+ os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\">" << endl;
+ os << "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" << endl;
+ os << "<title>" << i18n("Man output") << "</title>\n" << endl;
+ if ( !m_manCSSFile.isEmpty() )
+ os << "<link href=\"file:///" << m_manCSSFile << "\" type=\"text/css\" rel=\"stylesheet\">" << endl;
+ os << "</head>" << endl;
+ os << i18n("<body><h1>KDE Man Viewer Error</h1>") << errmsg << "</body>" << endl;
+ os << "</html>" << endl;
+
+ data(array);
+}
+
+void MANProtocol::outputMatchingPages(const QStringList &matchingPages)
+{
+ QByteArray array;
+ QTextStream os(array, IO_WriteOnly);
+ os.setEncoding(QTextStream::UnicodeUTF8);
+
+ os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\">" << endl;
+ os << "<html>\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"<<endl;
+ os << "<title>" << i18n("Man output") <<"</title>" << endl;
+ if ( !m_manCSSFile.isEmpty() )
+ os << "<link href=\"file:///" << m_manCSSFile << "\" type=\"text/css\" rel=\"stylesheet\">" << endl;
+ os << "</head>" <<endl;
+ os << "<body><h1>" << i18n("There is more than one matching man page.");
+ os << "</h1>\n<ul>\n";
+
+ int acckey=1;
+ for (QStringList::ConstIterator it = matchingPages.begin(); it != matchingPages.end(); ++it)
+ {
+ os<<"<li><a href='man:"<<(*it)<<"' accesskey='"<< acckey <<"'>"<< *it <<"</a><br>\n<br>\n";
+ acckey++;
+ }
+ os << "</ul>\n";
+ os << "<hr>\n";
+ os << "<p>" << i18n("Note: if you read a man page in your language,"
+ " be aware it can contain some mistakes or be obsolete."
+ " In case of doubt, you should have a look at the English version.") << "</p>";
+
+ os << "</body>\n</html>"<<endl;
+
+ data(array);
+ finished();
+}
+
+void MANProtocol::stat( const KURL& url)
+{
+ kdDebug(7107) << "ENTERING STAT " << url.url() << endl;
+
+ QString title, section;
+
+ if (!parseUrl(url.path(), title, section))
+ {
+ error(KIO::ERR_MALFORMED_URL, url.url());
+ return;
+ }
+
+ kdDebug(7107) << "URL " << url.url() << " parsed to title='" << title << "' section=" << section << endl;
+
+ UDSEntry entry;
+ UDSAtom atom;
+
+ atom.m_uds = UDS_NAME;
+ atom.m_long = 0;
+ atom.m_str = title;
+ entry.append(atom);
+
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_str = "";
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+
+ atom.m_uds = UDS_URL;
+ atom.m_long = 0;
+ QString newUrl = "man:"+title;
+ if (!section.isEmpty())
+ newUrl += QString("(%1)").arg(section);
+ atom.m_str = newUrl;
+ entry.append(atom);
+
+ atom.m_uds = UDS_MIME_TYPE;
+ atom.m_long = 0;
+ atom.m_str = "text/html";
+ entry.append(atom);
+
+ statEntry(entry);
+
+ finished();
+}
+
+
+extern "C"
+{
+
+ int KDE_EXPORT kdemain( int argc, char **argv ) {
+
+ KInstance instance("kio_man");
+
+ kdDebug(7107) << "STARTING " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_man protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ MANProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ kdDebug(7107) << "Done" << endl;
+
+ return 0;
+ }
+
+}
+
+void MANProtocol::mimetype(const KURL & /*url*/)
+{
+ mimeType("text/html");
+ finished();
+}
+
+static QString sectionName(const QString& section)
+{
+ if (section == "1")
+ return i18n("User Commands");
+ else if (section == "2")
+ return i18n("System Calls");
+ else if (section == "3")
+ return i18n("Subroutines");
+ else if (section == "3p")
+ return i18n("Perl Modules");
+ else if (section == "3n")
+ return i18n("Network Functions");
+ else if (section == "4")
+ return i18n("Devices");
+ else if (section == "5")
+ return i18n("File Formats");
+ else if (section == "6")
+ return i18n("Games");
+ else if (section == "7")
+ return i18n("Miscellaneous");
+ else if (section == "8")
+ return i18n("System Administration");
+ else if (section == "9")
+ return i18n("Kernel");
+ else if (section == "l")
+ return i18n("Local Documentation");
+ else if (section == "n")
+ return i18n("New");
+
+ return QString::null;
+}
+
+QStringList MANProtocol::buildSectionList(const QStringList& dirs) const
+{
+ QStringList l;
+
+ for (QStringList::ConstIterator it = section_names.begin();
+ it != section_names.end(); ++it)
+ {
+ for (QStringList::ConstIterator dir = dirs.begin();
+ dir != dirs.end(); ++dir)
+ {
+ QDir d((*dir)+"/man"+(*it));
+ if (d.exists())
+ {
+ l << *it;
+ break;
+ }
+ }
+ }
+ return l;
+}
+
+void MANProtocol::showMainIndex()
+{
+ QByteArray array;
+ QTextStream os(array, IO_WriteOnly);
+ os.setEncoding(QTextStream::UnicodeUTF8);
+
+ // print header
+ os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\">" << endl;
+ os << "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" << endl;
+ os << "<title>" << i18n("UNIX Manual Index") << "</title>" << endl;
+ if (!m_manCSSFile.isEmpty())
+ os << "<link href=\"file:///" << m_manCSSFile << "\" type=\"text/css\" rel=\"stylesheet\">" << endl;
+ os << "</head>" << endl;
+ os << "<body><h1>" << i18n("UNIX Manual Index") << "</h1>" << endl;
+
+ // ### TODO: why still the environment variable
+ const QString sectList = getenv("MANSECT");
+ QStringList sections;
+ if (sectList.isEmpty())
+ sections = buildSectionList(manDirectories());
+ else
+ sections = QStringList::split(':', sectList);
+
+ os << "<table>" << endl;
+
+ QStringList::ConstIterator it;
+ for (it = sections.begin(); it != sections.end(); ++it)
+ os << "<tr><td><a href=\"man:(" << *it << ")\" accesskey=\"" <<
+ (((*it).length()==1)?(*it):(*it).right(1))<<"\">" << i18n("Section ")
+ << *it << "</a></td><td>&nbsp;</td><td> " << sectionName(*it) << "</td></tr>" << endl;
+
+ os << "</table>" << endl;
+
+ // print footer
+ os << "</body></html>" << endl;
+
+ data(array);
+ finished();
+}
+
+void MANProtocol::constructPath(QStringList& constr_path, QStringList constr_catmanpath)
+{
+ QMap<QString, QString> manpath_map;
+ QMap<QString, QString> mandb_map;
+
+ // Add paths from /etc/man.conf
+ //
+ // Explicit manpaths may be given by lines starting with "MANPATH" or
+ // "MANDATORY_MANPATH" (depending on system ?).
+ // Mappings from $PATH to manpath are given by lines starting with
+ // "MANPATH_MAP"
+
+ QRegExp manpath_regex( "^MANPATH\\s" );
+ QRegExp mandatory_regex( "^MANDATORY_MANPATH\\s" );
+ QRegExp manpath_map_regex( "^MANPATH_MAP\\s" );
+ QRegExp mandb_map_regex( "^MANDB_MAP\\s" );
+ //QRegExp section_regex( "^SECTION\\s" );
+ QRegExp space_regex( "\\s+" ); // for parsing manpath map
+
+ QFile mc("/etc/man.conf"); // Caldera
+ if (!mc.exists())
+ mc.setName("/etc/manpath.config"); // SuSE, Debian
+ if (!mc.exists())
+ mc.setName("/etc/man.config"); // Mandrake
+
+ if (mc.open(IO_ReadOnly))
+ {
+ QTextStream is(&mc);
+ is.setEncoding(QTextStream::Locale);
+
+ while (!is.eof())
+ {
+ const QString line = is.readLine();
+ if ( manpath_regex.search(line, 0) == 0 )
+ {
+ const QString path = line.mid(8).stripWhiteSpace();
+ constr_path += path;
+ }
+ else if ( mandatory_regex.search(line, 0) == 0 )
+ {
+ const QString path = line.mid(18).stripWhiteSpace();
+ constr_path += path;
+ }
+ else if ( manpath_map_regex.search(line, 0) == 0 )
+ {
+ // The entry is "MANPATH_MAP <path> <manpath>"
+ const QStringList mapping =
+ QStringList::split(space_regex, line);
+
+ if ( mapping.count() == 3 )
+ {
+ const QString dir = QDir::cleanDirPath( mapping[1] );
+ const QString mandir = QDir::cleanDirPath( mapping[2] );
+
+ manpath_map[ dir ] = mandir;
+ }
+ }
+ else if ( mandb_map_regex.search(line, 0) == 0 )
+ {
+ // The entry is "MANDB_MAP <manpath> <catmanpath>"
+ const QStringList mapping =
+ QStringList::split(space_regex, line);
+
+ if ( mapping.count() == 3 )
+ {
+ const QString mandir = QDir::cleanDirPath( mapping[1] );
+ const QString catmandir = QDir::cleanDirPath( mapping[2] );
+
+ mandb_map[ mandir ] = catmandir;
+ }
+ }
+ /* sections are not used
+ else if ( section_regex.find(line, 0) == 0 )
+ {
+ if ( !conf_section.isEmpty() )
+ conf_section += ':';
+ conf_section += line.mid(8).stripWhiteSpace();
+ }
+ */
+ }
+ mc.close();
+ }
+
+ // Default paths
+ static const char *manpaths[] = {
+ "/usr/X11/man",
+ "/usr/X11R6/man",
+ "/usr/man",
+ "/usr/local/man",
+ "/usr/exp/man",
+ "/usr/openwin/man",
+ "/usr/dt/man",
+ "/opt/freetool/man",
+ "/opt/local/man",
+ "/usr/tex/man",
+ "/usr/www/man",
+ "/usr/lang/man",
+ "/usr/gnu/man",
+ "/usr/share/man",
+ "/usr/motif/man",
+ "/usr/titools/man",
+ "/usr/sunpc/man",
+ "/usr/ncd/man",
+ "/usr/newsprint/man",
+ NULL };
+
+
+ int i = 0;
+ while (manpaths[i]) {
+ if ( constr_path.findIndex( QString( manpaths[i] ) ) == -1 )
+ constr_path += QString( manpaths[i] );
+ i++;
+ }
+
+ // Directories in $PATH
+ // - if a manpath mapping exists, use that mapping
+ // - if a directory "<path>/man" or "<path>/../man" exists, add it
+ // to the man path (the actual existence check is done further down)
+
+ if ( ::getenv("PATH") ) {
+ const QStringList path =
+ QStringList::split( ":",
+ QString::fromLocal8Bit( ::getenv("PATH") ) );
+
+ for ( QStringList::const_iterator it = path.begin();
+ it != path.end();
+ ++it )
+ {
+ const QString dir = QDir::cleanDirPath( *it );
+ QString mandir = manpath_map[ dir ];
+
+ if ( !mandir.isEmpty() ) {
+ // a path mapping exists
+ if ( constr_path.findIndex( mandir ) == -1 )
+ constr_path += mandir;
+ }
+ else {
+ // no manpath mapping, use "<path>/man" and "<path>/../man"
+
+ mandir = dir + QString( "/man" );
+ if ( constr_path.findIndex( mandir ) == -1 )
+ constr_path += mandir;
+
+ int pos = dir.findRev( '/' );
+ if ( pos > 0 ) {
+ mandir = dir.left( pos ) + QString("/man");
+ if ( constr_path.findIndex( mandir ) == -1 )
+ constr_path += mandir;
+ }
+ }
+ QString catmandir = mandb_map[ mandir ];
+ if ( !mandir.isEmpty() )
+ {
+ if ( constr_catmanpath.findIndex( catmandir ) == -1 )
+ constr_catmanpath += catmandir;
+ }
+ else
+ {
+ // What is the default mapping?
+ catmandir = mandir;
+ catmandir.replace("/usr/share/","/var/cache/");
+ if ( constr_catmanpath.findIndex( catmandir ) == -1 )
+ constr_catmanpath += catmandir;
+ }
+ }
+ }
+}
+
+void MANProtocol::checkManPaths()
+{
+ static bool inited = false;
+
+ if (inited)
+ return;
+
+ inited = true;
+
+ const QString manpath_env = QString::fromLocal8Bit( ::getenv("MANPATH") );
+ //QString mansect_env = QString::fromLocal8Bit( ::getenv("MANSECT") );
+
+ // Decide if $MANPATH is enough on its own or if it should be merged
+ // with the constructed path.
+ // A $MANPATH starting or ending with ":", or containing "::",
+ // should be merged with the constructed path.
+
+ bool construct_path = false;
+
+ if ( manpath_env.isEmpty()
+ || manpath_env[0] == ':'
+ || manpath_env[manpath_env.length()-1] == ':'
+ || manpath_env.contains( "::" ) )
+ {
+ construct_path = true; // need to read config file
+ }
+
+ // Constucted man path -- consists of paths from
+ // /etc/man.conf
+ // default dirs
+ // $PATH
+ QStringList constr_path;
+ QStringList constr_catmanpath; // catmanpath
+
+ QString conf_section;
+
+ if ( construct_path )
+ {
+ constructPath(constr_path, constr_catmanpath);
+ }
+
+ m_mandbpath=constr_catmanpath;
+
+ // Merge $MANPATH with the constructed path to form the
+ // actual manpath.
+ //
+ // The merging syntax with ":" and "::" in $MANPATH will be
+ // satisfied if any empty string in path_list_env (there
+ // should be 1 or 0) is replaced by the constructed path.
+
+ const QStringList path_list_env = QStringList::split( ':', manpath_env , true );
+
+ for ( QStringList::const_iterator it = path_list_env.begin();
+ it != path_list_env.end();
+ ++it )
+ {
+ struct stat sbuf;
+
+ QString dir = (*it);
+
+ if ( !dir.isEmpty() ) {
+ // Add dir to the man path if it exists
+ if ( m_manpath.findIndex( dir ) == -1 ) {
+ if ( ::stat( QFile::encodeName( dir ), &sbuf ) == 0
+ && S_ISDIR( sbuf.st_mode ) )
+ {
+ m_manpath += dir;
+ }
+ }
+ }
+ else {
+ // Insert constructed path ($MANPATH was empty, or
+ // there was a ":" at an end or "::")
+
+ for ( QStringList::Iterator it2 = constr_path.begin();
+ it2 != constr_path.end();
+ it2++ )
+ {
+ dir = (*it2);
+
+ if ( !dir.isEmpty() ) {
+ if ( m_manpath.findIndex( dir ) == -1 ) {
+ if ( ::stat( QFile::encodeName( dir ), &sbuf ) == 0
+ && S_ISDIR( sbuf.st_mode ) )
+ {
+ m_manpath += dir;
+ }
+ }
+ }
+ }
+ }
+ }
+
+/* sections are not used
+ // Sections
+ QStringList m_mansect = QStringList::split( ':', mansect_env, true );
+
+ const char* default_sect[] =
+ { "1", "2", "3", "4", "5", "6", "7", "8", "9", "n", 0L };
+
+ for ( int i = 0; default_sect[i] != 0L; i++ )
+ if ( m_mansect.findIndex( QString( default_sect[i] ) ) == -1 )
+ m_mansect += QString( default_sect[i] );
+*/
+
+}
+
+
+//#define _USE_OLD_CODE
+
+#ifdef _USE_OLD_CODE
+#warning "using old code"
+#else
+
+// Define this, if you want to compile with qsort from stdlib.h
+// else the Qt Heapsort will be used.
+// Note, qsort seems to be a bit faster (~10%) on a large man section
+// eg. man section 3
+#define _USE_QSORT
+
+// Setup my own structure, with char pointers.
+// from now on only pointers are copied, no strings
+//
+// containing the whole path string,
+// the beginning of the man page name
+// and the length of the name
+struct man_index_t {
+ char *manpath; // the full path including man file
+ const char *manpage_begin; // pointer to the begin of the man file name in the path
+ int manpage_len; // len of the man file name
+};
+typedef man_index_t *man_index_ptr;
+
+#ifdef _USE_QSORT
+int compare_man_index(const void *s1, const void *s2)
+{
+ struct man_index_t *m1 = *(struct man_index_t **)s1;
+ struct man_index_t *m2 = *(struct man_index_t **)s2;
+ int i;
+ // Compare the names of the pages
+ // with the shorter length.
+ // Man page names are not '\0' terminated, so
+ // this is a bit tricky
+ if ( m1->manpage_len > m2->manpage_len)
+ {
+ i = qstrnicmp( m1->manpage_begin,
+ m2->manpage_begin,
+ m2->manpage_len);
+ if (!i)
+ return 1;
+ return i;
+ }
+
+ if ( m1->manpage_len < m2->manpage_len)
+ {
+ i = qstrnicmp( m1->manpage_begin,
+ m2->manpage_begin,
+ m1->manpage_len);
+ if (!i)
+ return -1;
+ return i;
+ }
+
+ return qstrnicmp( m1->manpage_begin,
+ m2->manpage_begin,
+ m1->manpage_len);
+}
+
+#else /* !_USE_QSORT */
+#warning using heapsort
+// Set up my own man page list,
+// with a special compare function to sort itself
+typedef QPtrList<struct man_index_t> QManIndexListBase;
+typedef QPtrListIterator<struct man_index_t> QManIndexListIterator;
+
+class QManIndexList : public QManIndexListBase
+{
+public:
+private:
+ int compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 )
+ {
+ struct man_index_t *m1 = (struct man_index_t *)s1;
+ struct man_index_t *m2 = (struct man_index_t *)s2;
+ int i;
+ // compare the names of the pages
+ // with the shorter length
+ if (m1->manpage_len > m2->manpage_len)
+ {
+ i = qstrnicmp(m1->manpage_begin,
+ m2->manpage_begin,
+ m2->manpage_len);
+ if (!i)
+ return 1;
+ return i;
+ }
+
+ if (m1->manpage_len > m2->manpage_len)
+ {
+
+ i = qstrnicmp(m1->manpage_begin,
+ m2->manpage_begin,
+ m1->manpage_len);
+ if (!i)
+ return -1;
+ return i;
+ }
+
+ return qstrnicmp(m1->manpage_begin,
+ m2->manpage_begin,
+ m1->manpage_len);
+ }
+};
+
+#endif /* !_USE_QSORT */
+#endif /* !_USE_OLD_CODE */
+
+
+
+
+void MANProtocol::showIndex(const QString& section)
+{
+ QByteArray array;
+ QTextStream os(array, IO_WriteOnly);
+ os.setEncoding(QTextStream::UnicodeUTF8);
+
+ // print header
+ os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\">" << endl;
+ os << "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" << endl;
+ os << "<title>" << i18n("UNIX Manual Index") << "</title>" << endl;
+ if ( !m_manCSSFile.isEmpty() )
+ os << "<link href=\"file:///" << m_manCSSFile << "\" type=\"text/css\" rel=\"stylesheet\">" << endl;
+ os << "</head>" << endl;
+ os << "<body><div class=\"secidxmain\">" << endl;
+ os << "<h1>" << i18n( "Index for Section %1: %2").arg(section).arg(sectionName(section)) << "</h1>" << endl;
+
+ // compose list of search paths -------------------------------------------------------------
+
+ checkManPaths();
+ infoMessage(i18n("Generating Index"));
+
+ // search for the man pages
+ QStringList pages = findPages( section, QString::null );
+
+ QMap<QString, QString> indexmap = buildIndexMap(section);
+
+ // print out the list
+ os << "<table>" << endl;
+
+#ifdef _USE_OLD_CODE
+ pages.sort();
+
+ QMap<QString, QString> pagemap;
+
+ QStringList::ConstIterator page;
+ for (page = pages.begin(); page != pages.end(); ++page)
+ {
+ QString fileName = *page;
+
+ stripExtension( &fileName );
+
+ pos = fileName.findRev('/');
+ if (pos > 0)
+ fileName = fileName.mid(pos+1);
+
+ if (!fileName.isEmpty())
+ pagemap[fileName] = *page;
+
+ }
+
+ for (QMap<QString,QString>::ConstIterator it = pagemap.begin();
+ it != pagemap.end(); ++it)
+ {
+ os << "<tr><td><a href=\"man:" << it.data() << "\">\n"
+ << it.key() << "</a></td><td>&nbsp;</td><td> "
+ << (indexmap.contains(it.key()) ? indexmap[it.key()] : "" )
+ << "</td></tr>" << endl;
+ }
+
+#else /* ! _USE_OLD_CODE */
+
+#ifdef _USE_QSORT
+
+ int listlen = pages.count();
+ man_index_ptr *indexlist = new man_index_ptr[listlen];
+ listlen = 0;
+
+#else /* !_USE_QSORT */
+
+ QManIndexList manpages;
+ manpages.setAutoDelete(TRUE);
+
+#endif /* _USE_QSORT */
+
+ QStringList::const_iterator page;
+ for (page = pages.begin(); page != pages.end(); ++page)
+ {
+ // I look for the beginning of the man page name
+ // i.e. "bla/pagename.3.gz" by looking for the last "/"
+ // Then look for the end of the name by searching backwards
+ // for the last ".", not counting zip extensions.
+ // If the len of the name is >0,
+ // store it in the list structure, to be sorted later
+
+ char *manpage_end;
+ struct man_index_t *manindex = new man_index_t;
+ manindex->manpath = strdup((*page).utf8());
+
+ manindex->manpage_begin = strrchr(manindex->manpath, '/');
+ if (manindex->manpage_begin)
+ {
+ manindex->manpage_begin++;
+ assert(manindex->manpage_begin >= manindex->manpath);
+ }
+ else
+ {
+ manindex->manpage_begin = manindex->manpath;
+ assert(manindex->manpage_begin >= manindex->manpath);
+ }
+
+ // Skip extension ".section[.gz]"
+
+ char *begin = (char*)(manindex->manpage_begin);
+ int len = strlen( begin );
+ char *end = begin+(len-1);
+
+ if ( len >= 3 && strcmp( end-2, ".gz" ) == 0 )
+ end -= 3;
+ else if ( len >= 2 && strcmp( end-1, ".Z" ) == 0 )
+ end -= 2;
+ else if ( len >= 2 && strcmp( end-1, ".z" ) == 0 )
+ end -= 2;
+ else if ( len >= 4 && strcmp( end-3, ".bz2" ) == 0 )
+ end -= 4;
+
+ while ( end >= begin && *end != '.' )
+ end--;
+
+ if ( end < begin )
+ manpage_end = 0;
+ else
+ manpage_end = end;
+
+ if (NULL == manpage_end)
+ {
+ // no '.' ending ???
+ // set the pointer past the end of the filename
+ manindex->manpage_len = (*page).length();
+ manindex->manpage_len -= (manindex->manpage_begin - manindex->manpath);
+ assert(manindex->manpage_len >= 0);
+ }
+ else
+ {
+ manindex->manpage_len = (manpage_end - manindex->manpage_begin);
+ assert(manindex->manpage_len >= 0);
+ }
+
+ if (0 < manindex->manpage_len)
+ {
+
+#ifdef _USE_QSORT
+
+ indexlist[listlen] = manindex;
+ listlen++;
+
+#else /* !_USE_QSORT */
+
+ manpages.append(manindex);
+
+#endif /* _USE_QSORT */
+
+ }
+ }
+
+ //
+ // Now do the sorting on the page names
+ // and the printout afterwards
+ // While printing avoid duplicate man page names
+ //
+
+ struct man_index_t dummy_index = {0l,0l,0};
+ struct man_index_t *last_index = &dummy_index;
+
+#ifdef _USE_QSORT
+
+ // sort and print
+ qsort(indexlist, listlen, sizeof(struct man_index_t *), compare_man_index);
+
+ QChar firstchar, tmp;
+ QString indexLine="<div class=\"secidxshort\">\n";
+ if (indexlist[0]->manpage_len>0)
+ {
+ firstchar=QChar((indexlist[0]->manpage_begin)[0]).lower();
+
+ const QString appendixstr = QString(
+ " [<a href=\"#%1\" accesskey=\"%2\">%3</a>]\n"
+ ).arg(firstchar).arg(firstchar).arg(firstchar);
+ indexLine.append(appendixstr);
+ }
+ os << "<tr><td class=\"secidxnextletter\"" << " colspan=\"3\">\n <a name=\""
+ << firstchar << "\">" << firstchar <<"</a>\n</td></tr>" << endl;
+
+ for (int i=0; i<listlen; i++)
+ {
+ struct man_index_t *manindex = indexlist[i];
+
+ // qstrncmp():
+ // "last_man" has already a \0 string ending, but
+ // "manindex->manpage_begin" has not,
+ // so do compare at most "manindex->manpage_len" of the strings.
+ if (last_index->manpage_len == manindex->manpage_len &&
+ !qstrncmp(last_index->manpage_begin,
+ manindex->manpage_begin,
+ manindex->manpage_len)
+ )
+ {
+ continue;
+ }
+
+ tmp=QChar((manindex->manpage_begin)[0]).lower();
+ if (firstchar != tmp)
+ {
+ firstchar = tmp;
+ os << "<tr><td class=\"secidxnextletter\"" << " colspan=\"3\">\n <a name=\""
+ << firstchar << "\">" << firstchar << "</a>\n</td></tr>" << endl;
+
+ const QString appendixstr = QString(
+ " [<a href=\"#%1\" accesskey=\"%2\">%3</a>]\n"
+ ).arg(firstchar).arg(firstchar).arg(firstchar);
+ indexLine.append(appendixstr);
+ }
+ os << "<tr><td><a href=\"man:"
+ << manindex->manpath << "\">\n";
+
+ ((char *)manindex->manpage_begin)[manindex->manpage_len] = '\0';
+ os << manindex->manpage_begin
+ << "</a></td><td>&nbsp;</td><td> "
+ << (indexmap.contains(manindex->manpage_begin) ? indexmap[manindex->manpage_begin] : "" )
+ << "</td></tr>" << endl;
+ last_index = manindex;
+ }
+ indexLine.append("</div>");
+
+ for (int i=0; i<listlen; i++) {
+ ::free(indexlist[i]->manpath); // allocated by strdup
+ delete indexlist[i];
+ }
+
+ delete [] indexlist;
+
+#else /* !_USE_QSORT */
+
+ manpages.sort(); // using
+
+ for (QManIndexListIterator mit(manpages);
+ mit.current();
+ ++mit )
+ {
+ struct man_index_t *manindex = mit.current();
+
+ // qstrncmp():
+ // "last_man" has already a \0 string ending, but
+ // "manindex->manpage_begin" has not,
+ // so do compare at most "manindex->manpage_len" of the strings.
+ if (last_index->manpage_len == manindex->manpage_len &&
+ !qstrncmp(last_index->manpage_begin,
+ manindex->manpage_begin,
+ manindex->manpage_len)
+ )
+ {
+ continue;
+ }
+
+ os << "<tr><td><a href=\"man:"
+ << manindex->manpath << "\">\n";
+
+ manindex->manpage_begin[manindex->manpage_len] = '\0';
+ os << manindex->manpage_begin
+ << "</a></td><td>&nbsp;</td><td> "
+ << (indexmap.contains(manindex->manpage_begin) ? indexmap[manindex->manpage_begin] : "" )
+ << "</td></tr>" << endl;
+ last_index = manindex;
+ }
+#endif /* _USE_QSORT */
+#endif /* _USE_OLD_CODE */
+
+ os << "</table></div>" << endl;
+
+ os << indexLine << endl;
+
+ // print footer
+ os << "</body></html>" << endl;
+
+ infoMessage(QString::null);
+ mimeType("text/html");
+ data(array);
+ finished();
+}
+
+void MANProtocol::listDir(const KURL &url)
+{
+ kdDebug( 7107 ) << "ENTER listDir: " << url.prettyURL() << endl;
+
+ QString title;
+ QString section;
+
+ if ( !parseUrl(url.path(), title, section) ) {
+ error( KIO::ERR_MALFORMED_URL, url.url() );
+ return;
+ }
+
+ QStringList list = findPages( section, QString::null, false );
+
+ UDSEntryList uds_entry_list;
+ UDSEntry uds_entry;
+ UDSAtom uds_atom;
+
+ uds_atom.m_uds = KIO::UDS_NAME; // we only do names...
+ uds_entry.append( uds_atom );
+
+ QStringList::Iterator it = list.begin();
+ QStringList::Iterator end = list.end();
+
+ for ( ; it != end; ++it ) {
+ stripExtension( &(*it) );
+
+ uds_entry[0].m_str = *it;
+ uds_entry_list.append( uds_entry );
+ }
+
+ listEntries( uds_entry_list );
+ finished();
+}
+
+void MANProtocol::getProgramPath()
+{
+ if (!mySgml2RoffPath.isEmpty())
+ return;
+
+ mySgml2RoffPath = KGlobal::dirs()->findExe("sgml2roff");
+ if (!mySgml2RoffPath.isEmpty())
+ return;
+
+ /* sgml2roff isn't found in PATH. Check some possible locations where it may be found. */
+ mySgml2RoffPath = KGlobal::dirs()->findExe("sgml2roff", QString(SGML2ROFF_DIRS));
+ if (!mySgml2RoffPath.isEmpty())
+ return;
+
+ /* Cannot find sgml2roff programm: */
+ outputError(i18n("Could not find the sgml2roff program on your system. Please install it, if necessary, and extend the search path by adjusting the environment variable PATH before starting KDE."));
+ finished();
+ exit();
+}
diff --git a/kioslave/man/kio_man.css b/kioslave/man/kio_man.css
new file mode 100644
index 000000000..8a9f378bc
--- /dev/null
+++ b/kioslave/man/kio_man.css
@@ -0,0 +1,21 @@
+body {background-color:#fffff}
+
+/*for the list of one manpage section*/
+.secidxshort {
+ display:block;
+ position:absolute;overflow:auto;
+ top:0px;bottom:95%;left:0;right:0;
+}
+.secidxmain {
+/*misfortunately accessing anchors in a scrollview
+ doesn't seem to work yet with konqi, so:*/
+/*
+ position:absolute;overflow:auto;
+ top:5%;bottom:0;left:0;right:0;
+*/
+}
+.secidxnextletter {
+ font-size:larger;
+ border-bottom:1px solid black;
+ text-align:center
+}
diff --git a/kioslave/man/kio_man.h b/kioslave/man/kio_man.h
new file mode 100644
index 000000000..f571082db
--- /dev/null
+++ b/kioslave/man/kio_man.h
@@ -0,0 +1,99 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2000 Matthias Hoelzer-Kluepfel <mhk@caldera.de>
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kio_man_h__
+#define __kio_man_h__
+
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+#include <qdict.h>
+#include <qbuffer.h>
+
+
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+
+class MANProtocol : public QObject, public KIO::SlaveBase
+{
+ Q_OBJECT
+
+public:
+
+ MANProtocol(const QCString &pool_socket, const QCString &app_socket);
+ virtual ~MANProtocol();
+
+ virtual void get(const KURL& url);
+ virtual void stat(const KURL& url);
+
+ virtual void mimetype(const KURL &url);
+ virtual void listDir(const KURL &url);
+
+ void outputError(const QString& errmsg);
+ void outputMatchingPages(const QStringList &matchingPages);
+
+ void showMainIndex();
+ void showIndex(const QString& section);
+
+ // the following two functions are the interface to man2html
+ void output(const char *insert);
+ char *readManPage(const char *filename);
+
+ static MANProtocol *self();
+
+private slots:
+ void slotGetStdOutput(KProcess*, char*, int);
+
+private:
+ void checkManPaths();
+ QStringList manDirectories();
+ QMap<QString, QString> buildIndexMap(const QString& section);
+ bool addWhatIs(QMap<QString, QString>& i, const QString& f, const QString& mark);
+ void parseWhatIs( QMap<QString, QString> &i, QTextStream &t, const QString &mark );
+ QStringList findPages(const QString& section,
+ const QString &title,
+ bool full_path = true);
+
+ void addToBuffer(const char *buffer, int buflen);
+ QString pageName(const QString& page) const;
+ QStringList buildSectionList(const QStringList& dirs) const;
+ void constructPath(QStringList& constr_path, QStringList constr_catmanpath);
+private:
+ static MANProtocol *_self;
+ QCString lastdir;
+
+ void findManPagesInSection(const QString &dir, const QString &title, bool full_path, QStringList &list);
+ QStringList m_manpath; ///< Path of man directories
+ QStringList m_mandbpath; ///< Path of catman directories
+ QStringList section_names;
+
+ QString myStdStream;
+ QString mySgml2RoffPath;
+ void getProgramPath();
+
+ QCString m_htmlPath; ///< Path to KDE resources, encoded for HTML
+ QCString m_cssPath; ///< Path to KDE resources, encoded for CSS
+ QBuffer m_outputBuffer; ///< Buffer for the output
+ QString m_manCSSFile; ///< Path to kio_man.css
+};
+
+
+#endif
diff --git a/kioslave/man/kio_man_test.cpp b/kioslave/man/kio_man_test.cpp
new file mode 100644
index 000000000..bfb78a652
--- /dev/null
+++ b/kioslave/man/kio_man_test.cpp
@@ -0,0 +1,38 @@
+
+
+#include <qobject.h>
+
+#include "kio_man.h"
+
+
+#include <kapplication.h>
+#include <klocale.h>
+
+
+class kio_man_test : public MANProtocol
+{
+ Q_OBJECT
+
+public:
+ kio_man_test(const QCString &pool_socket, const QCString &app_socket);
+
+protected:
+ virtual void data(int);
+
+};
+
+
+
+
+
+int main(int argc, char **argv)
+{
+ KApplication a( argc, argv , "p2");
+
+ MANProtocol testproto("/tmp/kiotest.in", "/tmp/kiotest.out");
+ testproto.showIndex("3");
+
+ return 0;
+}
+
+
diff --git a/kioslave/man/kmanpart.cpp b/kioslave/man/kmanpart.cpp
new file mode 100644
index 000000000..307a8c6b2
--- /dev/null
+++ b/kioslave/man/kmanpart.cpp
@@ -0,0 +1,115 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kmanpart.h"
+#include <qstring.h>
+
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kaboutdata.h>
+#include <kdeversion.h>
+
+extern "C"
+{
+ KDE_EXPORT void* init_libkmanpart()
+ {
+ return new KManPartFactory;
+ }
+}
+
+KInstance* KManPartFactory::s_instance = 0L;
+KAboutData* KManPartFactory::s_about = 0L;
+
+KManPartFactory::KManPartFactory( QObject* parent, const char* name )
+ : KParts::Factory( parent, name )
+{}
+
+KManPartFactory::~KManPartFactory()
+{
+ delete s_instance;
+ s_instance = 0L;
+ delete s_about;
+}
+
+KParts::Part* KManPartFactory::createPartObject( QWidget * parentWidget, const char* /*widgetName*/, QObject *,
+ const char* name, const char* /*className*/,const QStringList & )
+{
+ KManPart* part = new KManPart(parentWidget, name );
+ return part;
+}
+
+KInstance* KManPartFactory::instance()
+{
+ if( !s_instance )
+ {
+ s_about = new KAboutData( "kmanpart",
+ I18N_NOOP( "KMan" ), KDE_VERSION_STRING );
+ s_instance = new KInstance( s_about );
+ }
+ return s_instance;
+}
+
+
+KManPart::KManPart( QWidget * parent, const char * name )
+: KHTMLPart( parent, name )
+,m_job(0)
+{
+ KInstance * instance = new KInstance( "kmanpart" );
+ setInstance( instance );
+ m_extension=new KParts::BrowserExtension(this);
+}
+
+bool KManPart::openURL( const KURL &url )
+{
+ return KParts::ReadOnlyPart::openURL(url);
+}
+
+bool KManPart::openFile()
+{
+ if (m_job!=0)
+ m_job->kill();
+
+ begin();
+
+ KURL url;
+ url.setProtocol( "man" );
+ url.setPath( m_file );
+
+ m_job = KIO::get( url, true, false );
+ connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray &) ), SLOT( readData( KIO::Job *, const QByteArray &) ) );
+ connect( m_job, SIGNAL( result( KIO::Job * ) ), SLOT( jobDone( KIO::Job * ) ) );
+ return true;
+}
+
+void KManPart::readData(KIO::Job * , const QByteArray & data)
+{
+ write(data,data.size());
+}
+
+void KManPart::jobDone( KIO::Job *)
+{
+ m_job=0;
+ end();
+}
+
+#include "kmanpart.moc"
+
diff --git a/kioslave/man/kmanpart.desktop b/kioslave/man/kmanpart.desktop
new file mode 100644
index 000000000..1d9bb06bf
--- /dev/null
+++ b/kioslave/man/kmanpart.desktop
@@ -0,0 +1,91 @@
+[Desktop Entry]
+Type=Service
+Comment=Embeddable Troff Viewer
+Comment[af]=Inlegbare Troff Kyker
+Comment[ar]=مستعرض Troff المدمج
+Comment[be]=Убудаваны праглядальнік Troff
+Comment[bg]=Визуализатор за вграждане на Troff
+Comment[bn]=সন্নিবেশযোগ্য ট্রফ্ প্রদর্শক
+Comment[bs]=Ugradivi preglednik Troff datoteka
+Comment[ca]=Visor Troff encastable
+Comment[cs]=Komponenta pro zobrazování manuálových stránek
+Comment[csb]=Przezérnik lopków troff
+Comment[cy]=Gwelydd Troff Mewnadeiladwy
+Comment[da]=Indlejrbar Troff-fremviser
+Comment[de]=Eingebetteter Troff-Betrachter
+Comment[el]=Ενσωματώσιμος προβολέας Troff
+Comment[eo]=Enkonstruebla bildrigardilo
+Comment[es]=Visor empotrable de Troff
+Comment[et]=Põimitav Troff komponent
+Comment[eu]=Troff ikustailu txertagarria
+Comment[fa]=مشاهده‌گر Troff نهفتنی
+Comment[fi]=Upotettava Troff-näytin
+Comment[fr]=Afficheur troff incorporé
+Comment[fy]=Ynsletten Troff-werjefteprogramma
+Comment[ga]=Amharcán Inleabaithe troff
+Comment[gl]=Visor de Troff Incrustábel
+Comment[he]=מציג Troff בר־הטבעה
+Comment[hi]=एम्बेडेबल ट्राफ प्रदर्शक
+Comment[hr]=Ugradivi preglednik slika
+Comment[hu]=Beágyazható Troff-komponens
+Comment[is]=Ívefjanlegur troff skoðari
+Comment[it]=Visualizzatore integrabile di file Troff
+Comment[ja]=埋め込み Troff ビューア
+Comment[ka]=ჩაშენებული დამთვალიერებელი პროგრამა Troff
+Comment[kk]=Құрамына енгізілетін Troff қарау құралы
+Comment[km]=កម្មវិធី​មើល Troff ដែល​អាច​បង្កប់​បាន
+Comment[ko]=포함 가능한 Troff 뷰어
+Comment[lo]=ຄອມໂປເນັນຕົວສະແດງພາບທີ່ຝັ່ງໄດ້
+Comment[lt]=Įdedamas Troff žiūriklis
+Comment[lv]=Iegults Troff skatītājs
+Comment[mk]=Вгнездлив Troff прегледувач
+Comment[mn]=Холбох боломжит Troff-Харагч
+Comment[ms]=Pemapar Troff Boleh Benam
+Comment[mt]=Werrej integrat Troff
+Comment[nb]=Innebyggbar Troff-viser
+Comment[nds]=Kieker för Troff, de inbett warrn kann
+Comment[ne]=सम्मिलित गर्न सकिने ट्रफ दर्शक
+Comment[nl]=Ingebed Troff-weergaveprogramma
+Comment[nn]=Innebyggbar Troff-visar
+Comment[nso]=Selebeledi seo se Robaditswego sa Troff
+Comment[pa]=ਸ਼ਾਮਿਲ ਹੋਣਯੋਗ Troff ਦਰਸ਼ਕ
+Comment[pl]=Przeglądania plików troff
+Comment[pt]=Visualizador de Troff incorporado
+Comment[pt_BR]=Visualizar Troff integrado
+Comment[ro]=Componentă de vizualizare Troff înglobată
+Comment[ru]=Встраиваемая программа просмотра Troff
+Comment[rw]=Mugaragaza Troff Ishyirwamo
+Comment[se]=Vuojohahtti Troff-cájeheaddji
+Comment[sk]=Vložiteľný prehliadač Troff
+Comment[sl]=Vključen pregledovalnik za Troff
+Comment[sr]=Уградиви Troff приказивач
+Comment[sr@Latn]=Ugradivi Troff prikazivač
+Comment[sv]=Inbäddningsbar Troff-visare
+Comment[ta]=உட்பொதிந்த ட்ராஃப் காட்சி
+Comment[te]=పొదగదగ్గ ట్రాఫ్ వీక్షిణి
+Comment[tg]=Биношавандаи барномаи намоишгари Troff
+Comment[th]=โปรแกรมดู Troff ที่สามารถฝังตัวได้
+Comment[tr]=Gömülebilir Troff Görüntüleyici
+Comment[tt]=Quşılulı Troff-Kürsätkeç
+Comment[uk]=Вмонтований переглядач Troff
+Comment[uz]=Ichiga oʻrnatib boʻladigan Troff-faylini koʻruvchi
+Comment[uz@cyrillic]=Ичига ўрнатиб бўладиган Troff-файлини кўрувчи
+Comment[ven]=Tshivhoni tsha Troff tsho dzheniswaho
+Comment[vi]=Trình xem Troff nhúng được
+Comment[wa]=Ravalåve håyneu di fitchîs Troff
+Comment[xh]=Umboniseli we Troff Elungiselekayo
+Comment[zh_CN]=嵌入的 Troff 查看器
+Comment[zh_TW]=可嵌入的 Troff 檢視元件
+Comment[zu]=Umbukisi we-Troff Oshuthekiwe
+MimeType=application/x-troff;application/x-troff-man;
+Name=KManPart
+Name[eo]=KMan-parto
+Name[hi]=के-मेन-पार्ट
+Name[lo]=ຕົວຮງກພື້ນທີ່ທຳງານ - K
+Name[ne]=K म्यान भाग
+Name[pt_BR]=Componente KMan
+Name[ro]=Componentă KMan
+Name[sv]=Kman-del
+Name[te]=కెమేన్ భాగం
+ServiceTypes=KParts/ReadOnlyPart,Browser/View
+X-KDE-Library=libkmanpart
diff --git a/kioslave/man/kmanpart.h b/kioslave/man/kmanpart.h
new file mode 100644
index 000000000..f68b68784
--- /dev/null
+++ b/kioslave/man/kmanpart.h
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#ifndef KMANPART_H
+#define KMANPART_H
+
+#include <kparts/factory.h>
+#include <kparts/part.h>
+#include <kparts/browserextension.h>
+#include <khtml_part.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+
+#include <qcstring.h>
+
+class KInstance;
+class KAboutData;
+
+/**
+ * Man Page Viewer
+ * \todo: Why is it needed? Why is KHTML alone not possible?
+ */
+class KManPartFactory: public KParts::Factory
+{
+ Q_OBJECT
+ public:
+ KManPartFactory( QObject * parent = 0, const char * name = 0 );
+ virtual ~KManPartFactory();
+
+ virtual KParts::Part* createPartObject( QWidget * parentWidget, const char * widgetName ,
+ QObject* parent, const char* name, const char * classname,
+ const QStringList &args);
+
+ static KInstance * instance();
+
+ private:
+ static KInstance * s_instance;
+ static KAboutData * s_about;
+
+};
+
+class KManPart : public KHTMLPart
+{
+ Q_OBJECT
+ public:
+ KManPart( QWidget * parent, const char * name = 0L );
+ KParts::BrowserExtension * extension() {return m_extension;}
+
+ public slots:
+ virtual bool openURL( const KURL &url );
+ protected slots:
+ void readData(KIO::Job * , const QByteArray & data);
+ void jobDone( KIO::Job *);
+ protected:
+ virtual bool openFile();
+ KInstance *m_instance;
+ KParts::BrowserExtension * m_extension;
+ KIO::TransferJob *m_job;
+};
+
+#endif
+
diff --git a/kioslave/man/man.protocol b/kioslave/man/man.protocol
new file mode 100644
index 000000000..5df21f19d
--- /dev/null
+++ b/kioslave/man/man.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=kio_man
+protocol=man
+input=none
+output=filesystem
+reading=true
+listing=Name
+defaultMimetype=text/html
+determineMimetypeFromExtension=false
+DocPath=kioslave/man.html
+Icon=man
+Class=:local
diff --git a/kioslave/man/man2html.cpp b/kioslave/man/man2html.cpp
new file mode 100644
index 000000000..725fba36a
--- /dev/null
+++ b/kioslave/man/man2html.cpp
@@ -0,0 +1,5683 @@
+/*
+ This file is part of the KDE libraries
+
+ Copyright (C) 2005 Nicolas GOUTTE <goutte@kde.org>
+
+ ### TODO: who else?
+*/
+
+// Start of verbatim comment
+
+/*
+** This program was written by Richard Verhoeven (NL:5482ZX35)
+** at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
+**
+** Permission is granted to distribute, modify and use this program as long
+** as this comment is not removed or changed.
+*/
+
+// End of verbatim comment
+
+// kate: space-indent on; indent-width 4; replace-tabs on;
+
+/*
+ * man2html-linux-1.0/1.1
+ * This version modified for Redhat/Caldera linux - March 1996.
+ * Michael Hamilton <michael@actrix.gen.nz>.
+ *
+ * man2html-linux-1.2
+ * Added support for BSD mandoc pages - I didn't have any documentation
+ * on the mandoc macros, so I may have missed some.
+ * Michael Hamilton <michael@actrix.gen.nz>.
+ *
+ * vh-man2html-1.3
+ * Renamed to avoid confusion (V for Verhoeven, H for Hamilton).
+ *
+ * vh-man2html-1.4
+ * Now uses /etc/man.config
+ * Added support for compressed pages.
+ * Added "length-safe" string operations for client input parameters.
+ * More secure, -M secured, and client input string lengths checked.
+ *
+ */
+
+/*
+** If you want to use this program for your WWW server, adjust the line
+** which defines the CGIBASE or compile it with the -DCGIBASE='"..."' option.
+**
+** You have to adjust the built-in manpath to your local system. Note that
+** every directory should start and end with the '/' and that the first
+** directory should be "/" to allow a full path as an argument.
+**
+** The program first check if PATH_INFO contains some information.
+** If it does (t.i. man2html/some/thing is used), the program will look
+** for a manpage called PATH_INFO in the manpath.
+**
+** Otherwise the manpath is searched for the specified command line argument,
+** where the following options can be used:
+**
+** name name of manpage (csh, printf, xv, troff)
+** section the section (1 2 3 4 5 6 7 8 9 n l 1v ...)
+** -M path an extra directory to look for manpages (replaces "/")
+**
+** If man2html finds multiple manpages that satisfy the options, an index
+** is displayed and the user can make a choice. If only one page is
+** found, that page will be displayed.
+**
+** man2html will add links to the converted manpages. The function add_links
+** is used for that. At the moment it will add links as follows, where
+** indicates what should match to start with:
+** ^^^
+** Recognition Item Link
+** ----------------------------------------------------------
+** name(*) Manpage ../man?/name.*
+** ^
+** name@hostname Email address mailto:name@hostname
+** ^
+** method://string URL method://string
+** ^^^
+** www.host.name WWW server http://www.host.name
+** ^^^^
+** ftp.host.name FTP server ftp://ftp.host.name
+** ^^^^
+** <file.h> Include file file:/usr/include/file.h
+** ^^^
+**
+** Since man2html does not check if manpages, hosts or email addresses exist,
+** some links might not work. For manpages, some extra checks are performed
+** to make sure not every () pair creates a link. Also out of date pages
+** might point to incorrect places.
+**
+** The program will not allow users to get system specific files, such as
+** /etc/passwd. It will check that "man" is part of the specified file and
+** that "/../" isn't. Even if someone manages to get such file, man2html will
+** handle it like a manpage and will usually not produce any output (or crash).
+**
+** If you find any bugs when normal manpages are converted, please report
+** them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle
+** the manpage correct.
+**
+** Known bugs and missing features:
+**
+** * Equations are not converted at all.
+** * Tables are converted but some features are not possible in html.
+** * The tabbing environment is converted by counting characters and adding
+** spaces. This might go wrong (outside <PRE>)
+** * Some manpages rely on the fact that troff/nroff is used to convert
+** them and use features which are not descripted in the man manpages.
+** (definitions, calculations, conditionals, requests). I can't guarantee
+** that all these features work on all manpages. (I didn't have the
+** time to look through all the available manpages.)
+*/
+
+#ifdef SIMPLE_MAN2HTML
+ // We suppose that we run on a standard Linux
+# define HAVE_STRING_H 1
+# define HAVE_UNISTD_H 1
+#else
+# include <config.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#include <stdio.h>
+
+#include <qvaluestack.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qdatetime.h>
+
+#ifdef SIMPLE_MAN2HTML
+# include <stdlib.h>
+# include <iostream>
+# include <dirent.h>
+# include <sys/stat.h>
+# define kdDebug(x) cerr
+# define kdWarning(x) cerr << "WARNING "
+#else
+# include <qtextcodec.h>
+# include <kdebug.h>
+# include <kdeversion.h>
+#endif
+
+
+
+#include "man2html.h"
+
+using namespace std;
+
+#define NULL_TERMINATED(n) ((n) + 1)
+
+#define HUGE_STR_MAX 10000
+#define LARGE_STR_MAX 2000
+#define MED_STR_MAX 500
+#define SMALL_STR_MAX 100
+#define TINY_STR_MAX 10
+
+
+#if 1
+// The output is current too horrible to be called HTML 4.01
+#define DOCTYPE "<!DOCTYPE HTML>"
+#else
+#define DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+#endif
+
+/* mdoc(7) Bl/El lists to HTML list types */
+#define BL_DESC_LIST 1
+#define BL_BULLET_LIST 2
+#define BL_ENUM_LIST 4
+
+/* mdoc(7) Bd/Ed example(?) blocks */
+#define BD_LITERAL 1
+#define BD_INDENT 2
+
+static int s_nroff = 1; // NROFF mode by default
+
+static int mandoc_name_count = 0; /* Don't break on the first Nm */
+
+static char *stralloc(int len)
+{
+ /* allocate enough for len + NULL */
+ char *news = new char [len+1];
+#ifdef SIMPLE_MAN2HTML
+ if (!news)
+ {
+ cerr << "man2html: out of memory" << endl;
+ exit(EXIT_FAILURE);
+ }
+#else
+// modern compilers do not return a NULL pointer for a new
+#endif
+ return news;
+}
+
+static char *strlimitcpy(char *to, char *from, int n, int limit)
+{ /* Assumes space for limit plus a null */
+ const int len = n > limit ? limit : n;
+ qstrncpy(to, from, len + 1);
+ to[len] = '\0';
+ return to;
+}
+
+/* below this you should not change anything unless you know a lot
+** about this program or about troff.
+*/
+
+
+/// Structure for character definitions
+struct CSTRDEF {
+ int nr, slen;
+ const char *st;
+};
+
+
+
+const char NEWLINE[2]="\n";
+
+/**
+ * Class for defining strings and macros
+ */
+class StringDefinition
+{
+public:
+ StringDefinition( void ) : m_length(0) {}
+ StringDefinition( int len, const char* cstr ) : m_length( len ), m_output( cstr ) {}
+public:
+ int m_length; ///< Length of output text
+ QCString m_output; ///< Defined string
+};
+
+/**
+ * Class for defining number registers
+ * \note Not for internal read-only registers
+ */
+class NumberDefinition
+{
+ public:
+ NumberDefinition( void ) : m_value(0), m_increment(0) {}
+ NumberDefinition( int value ) : m_value( value ), m_increment(0) {}
+ NumberDefinition( int value, int incr) : m_value( value ), m_increment( incr ) {}
+ public:
+ int m_value; ///< value of number register
+ int m_increment; ///< Increment of number register
+ // ### TODO: display form (.af)
+};
+
+/**
+ * Map of character definitions
+ */
+static QMap<QCString,StringDefinition> s_characterDefinitionMap;
+
+/**
+ * Map of string variable and macro definitions
+ * \note String variables and macros are the same thing!
+ */
+static QMap<QCString,StringDefinition> s_stringDefinitionMap;
+
+/**
+ * Map of number registers
+ * \note Intern number registers (starting with a dot are not handled here)
+ */
+static QMap<QCString,NumberDefinition> s_numberDefinitionMap;
+
+static void fill_old_character_definitions( void );
+
+/**
+ * Initialize character variables
+ */
+static void InitCharacterDefinitions( void )
+{
+ fill_old_character_definitions();
+ // ### HACK: as we are converting to HTML too early, define characters with HTML references
+ s_characterDefinitionMap.insert( "&lt;-", StringDefinition( 1, "&larr;" ) ); // <-
+ s_characterDefinitionMap.insert( "-&gt;", StringDefinition( 1, "&rarr;" ) ); // ->
+ s_characterDefinitionMap.insert( "&lt;&gt;", StringDefinition( 1, "&harr;" ) ); // <>
+ s_characterDefinitionMap.insert( "&lt;=", StringDefinition( 1, "&le;" ) ); // <=
+ s_characterDefinitionMap.insert( "&gt;=", StringDefinition( 1, "&ge;" ) ); // >=
+ // End HACK
+}
+
+/**
+ * Initialize string variables
+ */
+static void InitStringDefinitions( void )
+{
+ // mdoc-only, see mdoc.samples(7)
+ s_stringDefinitionMap.insert( "<=", StringDefinition( 1, "&le;" ) );
+ s_stringDefinitionMap.insert( ">=", StringDefinition( 1, "&ge;" ) );
+ s_stringDefinitionMap.insert( "Rq", StringDefinition( 1, "&rdquo;" ) );
+ s_stringDefinitionMap.insert( "Lq", StringDefinition( 1, "&ldquo;" ) );
+ s_stringDefinitionMap.insert( "ua", StringDefinition( 1, "&circ" ) ); // Note this is different from \(ua
+ s_stringDefinitionMap.insert( "aa", StringDefinition( 1, "&acute;" ) );
+ s_stringDefinitionMap.insert( "ga", StringDefinition( 1, "`" ) );
+ s_stringDefinitionMap.insert( "q", StringDefinition( 1, "&quot;" ) );
+ s_stringDefinitionMap.insert( "Pi", StringDefinition( 1, "&pi;" ) );
+ s_stringDefinitionMap.insert( "Ne", StringDefinition( 1, "&ne;" ) );
+ s_stringDefinitionMap.insert( "Le", StringDefinition( 1, "&le;" ) );
+ s_stringDefinitionMap.insert( "Ge", StringDefinition( 1, "&ge;" ) );
+ s_stringDefinitionMap.insert( "Lt", StringDefinition( 1, "&lt;" ) );
+ s_stringDefinitionMap.insert( "Gt", StringDefinition( 1, "&gt;" ) );
+ s_stringDefinitionMap.insert( "Pm", StringDefinition( 1, "&plusmn;" ) );
+ s_stringDefinitionMap.insert( "If", StringDefinition( 1, "&infin;" ) );
+ s_stringDefinitionMap.insert( "Na", StringDefinition( 3, "NaN" ) );
+ s_stringDefinitionMap.insert( "Ba", StringDefinition( 1, "|" ) );
+ // end mdoc-only
+ // man(7)
+ s_stringDefinitionMap.insert( "Tm", StringDefinition( 1, "&trade;" ) ); // \*(TM
+ s_stringDefinitionMap.insert( "R", StringDefinition( 1, "&reg;" ) ); // \*R
+ // end man(7)
+ // Missing characters from man(7):
+ // \*S "Change to default font size"
+#ifndef SIMPLE_MAN2HTML
+ // Special KDE KIO man:
+ const QCString kdeversion(KDE_VERSION_STRING);
+ s_stringDefinitionMap.insert( ".KDE_VERSION_STRING", StringDefinition( kdeversion.length(), kdeversion ) );
+#endif
+}
+
+/**
+ * Initialize number registers
+ * \note Internal read-only registers are not handled here
+ */
+static void InitNumberDefinitions( void )
+{
+ // As the date number registers are more for end-users, better choose local time.
+ // Groff seems to support Gregorian dates only
+ QDate today( QDate::currentDate( Qt::LocalTime ) );
+ s_numberDefinitionMap.insert( "year", today.year() ); // Y2K-correct year
+ s_numberDefinitionMap.insert( "yr", today.year() - 1900 ); // Y2K-incorrect year
+ s_numberDefinitionMap.insert( "mo", today.month() );
+ s_numberDefinitionMap.insert( "dy", today.day() );
+ s_numberDefinitionMap.insert( "dw", today.dayOfWeek() );
+}
+
+
+#define V(A,B) ((A)*256+(B))
+
+//used in expand_char, e.g. for "\(bu"
+// see groff_char(7) for list
+static CSTRDEF standardchar[] = {
+ { V('*','*'), 1, "*" },
+ { V('*','A'), 1, "&Alpha;" },
+ { V('*','B'), 1, "&Beta;" },
+ { V('*','C'), 1, "&Xi;" },
+ { V('*','D'), 1, "&Delta;" },
+ { V('*','E'), 1, "&Epsilon;" },
+ { V('*','F'), 1, "&Phi;" },
+ { V('*','G'), 1, "&Gamma;" },
+ { V('*','H'), 1, "&Theta;" },
+ { V('*','I'), 1, "&Iota;" },
+ { V('*','K'), 1, "&Kappa;" },
+ { V('*','L'), 1, "&Lambda;" },
+ { V('*','M'), 1, "&Mu:" },
+ { V('*','N'), 1, "&Nu;" },
+ { V('*','O'), 1, "&Omicron;" },
+ { V('*','P'), 1, "&Pi;" },
+ { V('*','Q'), 1, "&Psi;" },
+ { V('*','R'), 1, "&Rho;" },
+ { V('*','S'), 1, "&Sigma;" },
+ { V('*','T'), 1, "&Tau;" },
+ { V('*','U'), 1, "&Upsilon;" },
+ { V('*','W'), 1, "&Omega;" },
+ { V('*','X'), 1, "&Chi;" },
+ { V('*','Y'), 1, "&Eta;" },
+ { V('*','Z'), 1, "&Zeta;" },
+ { V('*','a'), 1, "&alpha;"},
+ { V('*','b'), 1, "&beta;"},
+ { V('*','c'), 1, "&xi;"},
+ { V('*','d'), 1, "&delta;"},
+ { V('*','e'), 1, "&epsilon;"},
+ { V('*','f'), 1, "&phi;"},
+ { V('*','g'), 1, "&gamma;"},
+ { V('*','h'), 1, "&theta;"},
+ { V('*','i'), 1, "&iota;"},
+ { V('*','k'), 1, "&kappa;"},
+ { V('*','l'), 1, "&lambda;"},
+ { V('*','m'), 1, "&mu;" },
+ { V('*','n'), 1, "&nu;"},
+ { V('*','o'), 1, "&omicron;"},
+ { V('*','p'), 1, "&pi;"},
+ { V('*','q'), 1, "&psi;"},
+ { V('*','r'), 1, "&rho;"},
+ { V('*','s'), 1, "&sigma;"},
+ { V('*','t'), 1, "&tau;"},
+ { V('*','u'), 1, "&upsilon;"},
+ { V('*','w'), 1, "&omega;"},
+ { V('*','x'), 1, "&chi;"},
+ { V('*','y'), 1, "&eta;"},
+ { V('*','z'), 1, "&zeta;"},
+ { V('+','-'), 1, "&plusmn;" }, // not in groff_char(7)
+ { V('+','f'), 1, "&phi;"}, // phi1, we use the standard phi
+ { V('+','h'), 1, "&theta;"}, // theta1, we use the standard theta
+ { V('+','p'), 1, "&omega;"}, // omega1, we use the standard omega
+ { V('1','2'), 1, "&frac12;" },
+ { V('1','4'), 1, "&frac14;" },
+ { V('3','4'), 1, "&frac34;" },
+ { V('F','i'), 1, "&#xFB03;" }, // ffi ligature
+ { V('F','l'), 1, "&#xFB04;" }, // ffl ligature
+ { V('a','p'), 1, "~" },
+ { V('b','r'), 1, "|" },
+ { V('b','u'), 1, "&bull;" },
+ { V('b','v'), 1, "|" },
+ { V('c','i'), 1, "&#x25CB;" }, // circle ### TODO verify
+ { V('c','o'), 1, "&copy;" },
+ { V('c','t'), 1, "&cent;" },
+ { V('d','e'), 1, "&deg;" },
+ { V('d','g'), 1, "&dagger;" },
+ { V('d','i'), 1, "&divide;" },
+ { V('e','m'), 1, "&emdash;" },
+ { V('e','n'), 1, "&endash;"},
+ { V('e','q'), 1, "=" },
+ { V('e','s'), 1, "&empty;" },
+ { V('f','f'), 1, "&#0xFB00;" }, // ff ligature
+ { V('f','i'), 1, "&#0xFB01;" }, // fi ligature
+ { V('f','l'), 1, "&#0xFB02;" }, // fl ligature
+ { V('f','m'), 1, "&prime;" },
+ { V('g','a'), 1, "`" },
+ { V('h','y'), 1, "-" },
+ { V('l','c'), 2, "|&#175;" }, // ### TODO: not in groff_char(7)
+ { V('l','f'), 2, "|_" }, // ### TODO: not in groff_char(7)
+ { V('l','k'), 1, "<FONT SIZE=+2>{</FONT>" }, // ### TODO: not in groff_char(7)
+ { V('m','i'), 1, "-" }, // ### TODO: not in groff_char(7)
+ { V('m','u'), 1, "&times;" },
+ { V('n','o'), 1, "&not;" },
+ { V('o','r'), 1, "|" },
+ { V('p','l'), 1, "+" },
+ { V('r','c'), 2, "&#175;|" }, // ### TODO: not in groff_char(7)
+ { V('r','f'), 2, "_|" }, // ### TODO: not in groff_char(7)
+ { V('r','g'), 1, "&reg;" },
+ { V('r','k'), 1, "<FONT SIZE=+2>}</FONT>" }, // ### TODO: not in groff_char(7)
+ { V('r','n'), 1, "&oline;" },
+ { V('r','u'), 1, "_" },
+ { V('s','c'), 1, "&sect;" },
+ { V('s','l'), 1, "/" },
+ { V('s','q'), 2, "&#x25A1" }, // WHITE SQUARE
+ { V('t','s'), 1, "&#x03C2;" }, // FINAL SIGMA
+ { V('u','l'), 1, "_" },
+ { V('-','D'), 1, "&ETH;" },
+ { V('S','d'), 1, "&eth;" },
+ { V('T','P'), 1, "&THORN;" },
+ { V('T','p'), 1, "&thorn;" },
+ { V('A','E'), 1, "&AElig;" },
+ { V('a','e'), 1, "&aelig;" },
+ { V('O','E'), 1, "&OElig;" },
+ { V('o','e'), 1, "&oelig;" },
+ { V('s','s'), 1, "&szlig;" },
+ { V('\'','A'), 1, "&Aacute;" },
+ { V('\'','E'), 1, "&Eacute;" },
+ { V('\'','I'), 1, "&Iacute;" },
+ { V('\'','O'), 1, "&Oacute;" },
+ { V('\'','U'), 1, "&Uacute;" },
+ { V('\'','Y'), 1, "&Yacute;" },
+ { V('\'','a'), 1, "&aacute;" },
+ { V('\'','e'), 1, "&eacute;" },
+ { V('\'','i'), 1, "&iacute;" },
+ { V('\'','o'), 1, "&oacute;" },
+ { V('\'','u'), 1, "&uacute;" },
+ { V('\'','y'), 1, "&yacute;" },
+ { V(':','A'), 1, "&Auml;" },
+ { V(':','E'), 1, "&Euml;" },
+ { V(':','I'), 1, "&Iuml;" },
+ { V(':','O'), 1, "&Ouml;" },
+ { V(':','U'), 1, "&Uuml;" },
+ { V(':','a'), 1, "&auml;" },
+ { V(':','e'), 1, "&euml;" },
+ { V(':','i'), 1, "&iuml;" },
+ { V(':','o'), 1, "&ouml;" },
+ { V(':','u'), 1, "&uuml;" },
+ { V(':','y'), 1, "&yuml;" },
+ { V('^','A'), 1, "&Acirc;" },
+ { V('^','E'), 1, "&Ecirc;" },
+ { V('^','I'), 1, "&Icirc;" },
+ { V('^','O'), 1, "&Ocirc;" },
+ { V('^','U'), 1, "&Ucirc;" },
+ { V('^','a'), 1, "&acirc;" },
+ { V('^','e'), 1, "&ecirc;" },
+ { V('^','i'), 1, "&icirc;" },
+ { V('^','o'), 1, "&ocirc;" },
+ { V('^','u'), 1, "&ucirc;" },
+ { V('`','A'), 1, "&Agrave;" },
+ { V('`','E'), 1, "&Egrave;" },
+ { V('`','I'), 1, "&Igrave;" },
+ { V('`','O'), 1, "&Ograve;" },
+ { V('`','U'), 1, "&Ugrave;" },
+ { V('`','a'), 1, "&agrave;" },
+ { V('`','e'), 1, "&egrave;" },
+ { V('`','i'), 1, "&igrave;" },
+ { V('`','o'), 1, "&ograve;" },
+ { V('`','u'), 1, "&ugrave;" },
+ { V('~','A'), 1, "&Atilde;" },
+ { V('~','N'), 1, "&Ntilde;" },
+ { V('~','O'), 1, "&Otilde;" },
+ { V('~','a'), 1, "&atilde" },
+ { V('~','n'), 1, "&ntidle;" },
+ { V('~','o'), 1, "&otidle;" },
+ { V(',','C'), 1, "&Ccedil;" },
+ { V(',','c'), 1, "&ccedil;" },
+ { V('/','L'), 1, "&#x0141;" },
+ { V('/','l'), 1, "&#x0142;" },
+ { V('/','O'), 1, "&Oslash;" },
+ { V('/','o'), 1, "&oslash;" },
+ { V('o','A'), 1, "&Aring;" },
+ { V('o','a'), 1, "&aring;" },
+ { V('a','"'), 1, "\"" },
+ { V('a','-'), 1, "&macr;" },
+ { V('a','.'), 1, "." },
+ { V('a','^'), 1, "&circ;" },
+ { V('a','a'), 1, "&acute;" },
+ { V('a','b'), 1, "`" },
+ { V('a','c'), 1, "&cedil;" },
+ { V('a','d'), 1, "&uml;" },
+ { V('a','h'), 1, "&#x02C2;" }, // caron
+ { V('a','o'), 1, "&#x02DA;" }, // ring
+ { V('a','~'), 1, "&tilde;" },
+ { V('h','o'), 1, "&#x02DB;" }, // ogonek
+ { V('.','i'), 1, "&#x0131;" }, // dot less i
+ { V('C','s'), 1, "&curren;" },
+ { V('D','o'), 1, "$" },
+ { V('P','o'), 1, "&pound;" },
+ { V('Y','e'), 1, "&yen;" },
+ { V('F','n'), 1, "&fnof;" },
+ { V('F','o'), 1, "&laquo;" },
+ { V('F','c'), 1, "&raquo;" },
+ { V('f','o'), 1, "&#x2039;" }, // single left guillemet
+ { V('f','c'), 1, "&#x203A;" }, // single right guillemet
+ { V('r','!'), 1, "&iecl;" },
+ { V('r','?'), 1, "&iquest;" },
+ { V('O','f'), 1, "&ordf" },
+ { V('O','m'), 1, "&ordm;" },
+ { V('p','c'), 1, "&middot;" },
+ { V('S','1'), 1, "&sup1;" },
+ { V('S','2'), 1, "&sup2;" },
+ { V('S','3'), 1, "&sup3;" },
+ { V('<','-'), 1, "&larr;" },
+ { V('-','>'), 1, "&rarr;" },
+ { V('<','>'), 1, "&harr;" },
+ { V('d','a'), 1, "&darr;" },
+ { V('u','a'), 1, "&uarr;" },
+ { V('l','A'), 1, "&lArr;" },
+ { V('r','A'), 1, "&rArr;" },
+ { V('h','A'), 1, "&hArr;" },
+ { V('d','A'), 1, "&dArr;" },
+ { V('u','A'), 1, "&uArr;" },
+ { V('b','a'), 1, "|" },
+ { V('b','b'), 1, "&brvbar;" },
+ { V('t','m'), 1, "&trade;" },
+ { V('d','d'), 1, "&Dagger;" },
+ { V('p','s'), 1, "&para;" },
+ { V('%','0'), 1, "&permil;" },
+ { V('f','/'), 1, "&frasl;" }, // Fraction slash
+ { V('s','d'), 1, "&Prime;" },
+ { V('h','a'), 1, "^" },
+ { V('t','i'), 1, "&tidle;" },
+ { V('l','B'), 1, "[" },
+ { V('r','B'), 1, "]" },
+ { V('l','C'), 1, "{" },
+ { V('r','C'), 1, "}" },
+ { V('l','a'), 1, "&lt;" },
+ { V('r','a'), 1, "&gt;" },
+ { V('l','h'), 1, "&le;" },
+ { V('r','h'), 1, "&ge;" },
+ { V('B','q'), 1, "&bdquo;" },
+ { V('b','q'), 1, "&sbquo;" },
+ { V('l','q'), 1, "&ldquo;" },
+ { V('r','q'), 1, "&rdquo;" },
+ { V('o','q'), 1, "&lsquo;" },
+ { V('c','q'), 1, "&rsquo;" },
+ { V('a','q'), 1, "'" },
+ { V('d','q'), 1, "\"" },
+ { V('a','t'), 1, "@" },
+ { V('s','h'), 1, "#" },
+ { V('r','s'), 1, "\\" },
+ { V('t','f'), 1, "&there4;" },
+ { V('~','~'), 1, "&cong;" },
+ { V('~','='), 1, "&asymp;" },
+ { V('!','='), 1, "&ne;" },
+ { V('<','='), 1, "&le;" },
+ { V('=','='), 1, "&equiv;" },
+ { V('=','~'), 1, "&cong;" }, // ### TODO: verify
+ { V('>','='), 1, "&ge;" },
+ { V('A','N'), 1, "&and;" },
+ { V('O','R'), 1, "&or;" },
+ { V('t','e'), 1, "&exist;" },
+ { V('f','a'), 1, "&forall;" },
+ { V('A','h'), 1, "&alefsym;" },
+ { V('I','m'), 1, "&image;" },
+ { V('R','e'), 1, "&real;" },
+ { V('i','f'), 1, "&infin;" },
+ { V('m','d'), 1, "&sdot;" },
+ { V('m','o'), 1, "&#x2206;" }, // element ### TODO verify
+ { V('n','m'), 1, "&notin;" },
+ { V('p','t'), 1, "&prop;" },
+ { V('p','p'), 1, "&perp;" },
+ { V('s','b'), 1, "&sub;" },
+ { V('s','p'), 1, "&sup;" },
+ { V('i','b'), 1, "&sube;" },
+ { V('i','p'), 1, "&supe;" },
+ { V('i','s'), 1, "&int;" },
+ { V('s','r'), 1, "&radic;" },
+ { V('p','d'), 1, "&part;" },
+ { V('c','*'), 1, "&otimes;" },
+ { V('c','+'), 1, "&oplus;" },
+ { V('c','a'), 1, "&cap;" },
+ { V('c','u'), 1, "&cup;" },
+ { V('g','r'), 1, "V" }, // gradient ### TODO Where in Unicode?
+ { V('C','R'), 1, "&crarr;" },
+ { V('s','t'), 2, "-)" }, // "such that" ### TODO Where in Unicode?
+ { V('/','_'), 1, "&ang;" },
+ { V('w','p'), 1, "&weierp;" },
+ { V('l','z'), 1, "&loz;" },
+ { V('a','n'), 1, "-" }, // "horizontal arrow extension" ### TODO Where in Unicode?
+};
+
+/* default: print code */
+
+
+/* static char eqndelimopen=0, eqndelimclose=0; */
+static char escapesym='\\', nobreaksym='\'', controlsym='.', fieldsym=0, padsym=0;
+
+static char *buffer=NULL;
+static int buffpos=0, buffmax=0;
+static bool scaninbuff=false;
+static int itemdepth=0;
+static int section=0;
+static int dl_set[20]= { 0 };
+static bool still_dd=0;
+static int tabstops[20] = { 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96 };
+static int maxtstop=12;
+static int curpos=0;
+
+static char *scan_troff(char *c, bool san, char **result);
+static char *scan_troff_mandoc(char *c, bool san, char **result);
+
+static QValueList<char*> s_argumentList;
+
+static QCString htmlPath, cssPath;
+
+static QCString s_dollarZero; // Value of $0
+
+void setResourcePath(const QCString& _htmlPath, const QCString& _cssPath)
+{
+ htmlPath=_htmlPath;
+ cssPath=_cssPath;
+}
+
+static void fill_old_character_definitions( void )
+{
+ for (size_t i = 0; i < sizeof(standardchar)/sizeof(CSTRDEF); i++)
+ {
+ const int nr = standardchar[i].nr;
+ const char temp[3] = { nr / 256, nr % 256, 0 };
+ QCString name( temp );
+ s_characterDefinitionMap.insert( name, StringDefinition( standardchar[i].slen, standardchar[i].st ) );
+ }
+}
+
+static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
+static int no_newline_output=0;
+static int newline_for_fun=0;
+static bool output_possible=false;
+
+static const char *includedirs[] = {
+ "/usr/include",
+ "/usr/include/sys",
+ "/usr/local/include",
+ "/opt/local/include",
+ "/usr/ccs",
+ "/usr/X11R6/include",
+ "/usr/openwin/include",
+ "/usr/include/g++",
+ 0
+};
+
+static bool ignore_links=false;
+
+static void add_links(char *c)
+{
+ /*
+ ** Add the links to the output.
+ ** At the moment the following are recognized:
+ **
+ ** name(*) -> ../man?/name.*
+ ** method://string -> method://string
+ ** www.host.name -> http://www.host.name
+ ** ftp.host.name -> ftp://ftp.host.name
+ ** name@host -> mailto:name@host
+ ** <name.h> -> file:/usr/include/name.h (guess)
+ **
+ ** Other possible links to add in the future:
+ **
+ ** /dir/dir/file -> file:/dir/dir/file
+ */
+ if (ignore_links)
+ {
+ output_real(c);
+ return;
+ }
+
+ int i,j,nr;
+ char *f, *g,*h;
+ const int numtests=6; // Nmber of tests
+ char *idtest[numtests]; // url, mailto, www, ftp, manpage, C header file
+ bool ok;
+ /* search for (section) */
+ nr=0;
+ idtest[0]=strstr(c+1,"://");
+ idtest[1]=strchr(c+1,'@');
+ idtest[2]=strstr(c,"www.");
+ idtest[3]=strstr(c,"ftp.");
+ idtest[4]=strchr(c+1,'(');
+ idtest[5]=strstr(c+1,".h&gt;");
+ for (i=0; i<numtests; ++i) nr += (idtest[i]!=NULL);
+ while (nr) {
+ j=-1;
+ for (i=0; i<numtests; i++)
+ if (idtest[i] && (j<0 || idtest[i]<idtest[j])) j=i;
+ switch (j) {
+ case 5: { /* <name.h> */
+ f=idtest[5];
+ h=f+2;
+ g=f;
+ while (g>c && g[-1]!=';') g--;
+ bool wrote_include = false;
+
+ if (g!=c) {
+
+ QCString dir;
+ QCString file(g, h - g + 1);
+ file = file.stripWhiteSpace();
+ for (int index = 0; includedirs[index]; index++) {
+ QCString str = QCString(includedirs[index]) + "/" + file;
+ if (!access(str, R_OK)) {
+ dir = includedirs[index];
+ break;
+ }
+ }
+ if (!dir.isEmpty()) {
+
+ char t;
+ t=*g;
+ *g=0;
+ output_real(c);
+ *g=t;*h=0;
+
+ QCString str;
+ str.sprintf("<A HREF=\"file:%s/%s\">%s</A>&gt;", dir.data(), file.data(), file.data());
+ output_real(str.data());
+ c=f+6;
+ wrote_include = true;
+ }
+
+ }
+
+ if (!wrote_include) {
+ f[5]=0;
+ output_real(c);
+ f[5]=';';
+ c=f+5;
+ }
+ }
+ break;
+ case 4: /* manpage */
+ f=idtest[j];
+ /* check section */
+ g=strchr(f,')');
+ // The character before f must alphanumeric, the end of a HTML tag or the end of a &nbsp;
+ if (g!=NULL && f>c && (g-f)<12 && (isalnum(f[-1]) || f[-1]=='>' || ( f[-1] == ';' ) ) &&
+ isdigit(f[1]) && f[1]!='0' && ((g-f)<=2 || isalpha(f[2])))
+ {
+ ok = TRUE;
+ h = f+2;
+ while (h<g)
+ {
+ if (!isalnum(*h++))
+ {
+ ok = FALSE;
+ break;
+ }
+ }
+ }
+ else
+ ok = false;
+
+ h = f - 1;
+ if ( ok )
+ {
+ // Skip &nbsp;
+ kdDebug(7107) << "BEFORE SECTION:" << *h << endl;
+ if ( ( h > c + 5 ) && ( ! memcmp( h-5, "&nbsp;", 6 ) ) )
+ {
+ h -= 6;
+ kdDebug(7107) << "Skip &nbsp;" << endl;
+ }
+ else if ( *h == ';' )
+ {
+ // Not a non-breaking space, so probably not ok
+ ok = false;
+ }
+ }
+
+ if (ok)
+ {
+ /* this might be a link */
+ /* skip html makeup */
+ while (h>c && *h=='>') {
+ while (h!=c && *h!='<') h--;
+ if (h!=c) h--;
+ }
+ if (isalnum(*h)) {
+ char t,sec, *e;
+ QString subsec; // ### TODO avoid using QString, as we do not know the encoding
+ QString fstr(f); // ### TODO avoid using QString, as we do not know the encoding
+ e=h+1;
+ sec=f[1];
+ subsec=f[2];
+ int index = fstr.find(')', 2);
+ if (index != -1)
+ subsec = fstr.mid(2, index - 2);
+ else // No closing ')' found, take first character as subsection.
+ subsec = fstr.mid(2, 1);
+ while (h>c && (isalnum(h[-1]) || h[-1]=='_'
+ || h[-1]==':' || h[-1]=='-' || h[-1]=='.'))
+ h--;
+ t=*h;
+ *h='\0';
+ output_real(c);
+ *h=t;
+ t=*e;
+ *e='\0';
+ QCString str;
+ if (subsec.isEmpty())
+ str.sprintf("<A HREF=\"man:%s(%c)\">%s</A>", h, sec, h);
+ else
+ str.sprintf("<A HREF=\"man:%s(%c%s)\">%s</A>", h, sec, subsec.lower().latin1(), h);
+ output_real(str.data());
+ *e=t;
+ c=e;
+ }
+ }
+ *f='\0';
+ output_real(c);
+ *f='(';
+ idtest[4]=f-1;
+ c=f;
+ break; /* manpage */
+ case 3: /* ftp */
+ case 2: /* www */
+ g=f=idtest[j];
+ while (*g && (isalnum(*g) || *g=='_' || *g=='-' || *g=='+' ||
+ *g=='.' || *g=='/')) g++;
+ if (g[-1]=='.') g--;
+ if (g-f>4) {
+ char t;
+ t=*f; *f='\0';
+ output_real(c);
+ *f=t; t=*g;*g='\0';
+ QCString str;
+ str.sprintf("<A HREF=\"%s://%s\">%s</A>", ((j==3)?"ftp":"http"), f, f);
+ output_real(str.data());
+ *g=t;
+ c=g;
+ } else {
+ f[3]='\0';
+ output_real(c);
+ c=f+3;
+ f[3]='.';
+ }
+ break;
+ case 1: /* mailto */
+ g=f=idtest[1];
+ while (g>c && (isalnum(g[-1]) || g[-1]=='_' || g[-1]=='-' ||
+ g[-1]=='+' || g[-1]=='.' || g[-1]=='%')) g--;
+ if (g-7>=c && g[-1]==':')
+ {
+ // We have perhaps an email address starting with mailto:
+ if (!qstrncmp("mailto:",g-7,7))
+ g-=7;
+ }
+ h=f+1;
+ while (*h && (isalnum(*h) || *h=='_' || *h=='-' || *h=='+' ||
+ *h=='.')) h++;
+ if (*h=='.') h--;
+ if (h-f>4 && f-g>1) {
+ char t;
+ t=*g;
+ *g='\0';
+ output_real(c);
+ *g=t;t=*h;*h='\0';
+ QCString str;
+ str.sprintf("<A HREF=\"mailto:%s\">%s</A>", g, g);
+ output_real(str.data());
+ *h=t;
+ c=h;
+ } else {
+ *f='\0';
+ output_real(c);
+ *f='@';
+ idtest[1]=c;
+ c=f;
+ }
+ break;
+ case 0: /* url */
+ g=f=idtest[0];
+ while (g>c && isalpha(g[-1]) && islower(g[-1])) g--;
+ h=f+3;
+ while (*h && !isspace(*h) && *h!='<' && *h!='>' && *h!='"' &&
+ *h!='&') h++;
+ if (f-g>2 && f-g<7 && h-f>3) {
+ char t;
+ t=*g;
+ *g='\0';
+ output_real(c);
+ *g=t; t=*h; *h='\0';
+ QCString str;
+ str.sprintf("<A HREF=\"%s\">%s</A>", g, g);
+ output_real(str.data());
+ *h=t;
+ c=h;
+ } else {
+ f[1]='\0';
+ output_real(c);
+ f[1]='/';
+ c=f+1;
+ }
+ break;
+ default:
+ break;
+ }
+ nr=0;
+ if (idtest[0] && idtest[0]<=c) idtest[0]=strstr(c+1,"://");
+ if (idtest[1] && idtest[1]<=c) idtest[1]=strchr(c+1,'@');
+ if (idtest[2] && idtest[2]<c) idtest[2]=strstr(c,"www.");
+ if (idtest[3] && idtest[3]<c) idtest[3]=strstr(c,"ftp.");
+ if (idtest[4] && idtest[4]<=c) idtest[4]=strchr(c+1,'(');
+ if (idtest[5] && idtest[5]<=c) idtest[5]=strstr(c+1,".h&gt;");
+ for (i=0; i<numtests; i++) nr += (idtest[i]!=NULL);
+ }
+ output_real(c);
+}
+
+static QCString current_font;
+static int current_size=0;
+static int fillout=1;
+
+static void out_html(const char *c)
+{
+ if (!c) return;
+
+ // Added, probably due to the const?
+ char *c2 = qstrdup(c);
+ char *c3 = c2;
+
+ static int obp=0;
+
+ if (no_newline_output) {
+ int i=0;
+ no_newline_output=1;
+ while (c2[i]) {
+ if (!no_newline_output) c2[i-1]=c2[i];
+ if (c2[i]=='\n') no_newline_output=0;
+ i++;
+ }
+ if (!no_newline_output) c2[i-1]=0;
+ }
+ if (scaninbuff) {
+ while (*c2) {
+ if (buffpos>=buffmax) {
+ char *h = new char[buffmax*2];
+
+#ifdef SIMPLE_MAN2HTML
+ if (!h)
+ {
+ cerr << "Memory full, cannot output!" << endl;
+ exit(1);
+ }
+#else
+// modern compiler do not return a NULL for a new
+#endif
+ memcpy(h, buffer, buffmax);
+ delete [] buffer;
+ buffer=h;
+ buffmax=buffmax*2;
+ }
+ buffer[buffpos++]=*c2++;
+ }
+ } else
+ if (output_possible) {
+ while (*c2) {
+ outbuffer[obp++]=*c2;
+ if (*c=='\n' || obp >= HUGE_STR_MAX) {
+ outbuffer[obp]='\0';
+ add_links(outbuffer);
+ obp=0;
+ }
+ c2++;
+ }
+ }
+ delete [] c3;
+}
+
+static QCString set_font( const QCString& name )
+{
+ // Every font but R (Regular) creates <span> elements
+ QCString markup;
+ if ( current_font != "R" && !current_font.isEmpty() )
+ markup += "</span>";
+ const uint len = name.length();
+ bool fontok = true;
+ if ( len == 1 )
+ {
+ const char lead = name[0];
+ switch (lead)
+ {
+ case 'P': // Palatino?
+ case 'R': break; // regular, do nothing
+ case 'I': markup += "<span style=\"font-style:italic\">"; break;
+ case 'B': markup += "<span style=\"font-weight:bold\">"; break;
+ case 'L': markup += "<span style=\"font-family:monospace\">"; break; // ### What's L?
+ default: fontok = false;
+ }
+ }
+ else if ( len == 2 )
+ {
+ if ( name == "BI" )
+ markup += "<span style=\"font-style:italic;font-weight:bold\">";
+ // Courier
+ else if ( name == "CR" )
+ markup += "<span style=\"font-family:monospace\">";
+ else if ( name == "CW" ) // CW is used by pod2man(1) (alias PerlDoc)
+ markup += "<span style=\"font-family:monospace\">";
+ else if ( name == "CI" )
+ markup += "<span style=\"font-family:monospace;font-style:italic\">";
+ else if ( name == "CB" )
+ markup += "<span style=\"font-family:monospace;font-weight:bold\">";
+ // Times
+ else if ( name == "TR" )
+ markup += "<span style=\"font-family:serif\">";
+ else if ( name == "TI" )
+ markup += "<span style=\"font-family:serif;font-style:italic\">";
+ else if ( name == "TB" )
+ markup += "<span style=\"font-family:serif;font-weight:bold\">";
+ // Helvetica
+ else if ( name == "HR" )
+ markup += "<span style=\"font-family:sans-serif\">";
+ else if ( name == "HI" )
+ markup += "<span style=\"font-family:sans-serif;font-style:italic\">";
+ else if ( name == "HB" )
+ markup += "<span style=\"font-family:sans-serif;font-weight:bold\">";
+ else
+ fontok = false;
+ }
+ else if ( len == 3 )
+ {
+ if ( name == "CBI" )
+ markup += "<span style=\"font-family:monospace;font-style:italic;font-weight:bold\">";
+ else if ( name == "TBI" )
+ markup += "<span style=\"font-family:serif;font-style:italic;font-weight:bold\">";
+ else if ( name == "HBI" )
+ markup += "<span style=\"font-family:sans-serif;font-style:italic;font-weight:bold\">";
+ }
+ if (fontok)
+ current_font = name;
+ else
+ current_font = "R"; // Still nothing, then it is 'R' (Regular)
+ return markup;
+}
+
+/// \deprecated
+static QCString set_font( const char ch )
+#ifndef SIMPLE_MAN2HTML
+ KDE_DEPRECATED;
+
+static QCString set_font( const char ch )
+#endif
+{
+ const QCString name = &ch;
+ return set_font( name );
+}
+
+static QCString change_to_size(int nr)
+{
+ switch (nr)
+ {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9': nr=nr-'0'; break;
+ case '\0': break;
+ default: nr=current_size+nr; if (nr>9) nr=9; if (nr< -9) nr=-9; break;
+ }
+ if ( nr == current_size )
+ return "";
+ const QCString font ( current_font );
+ QCString markup;
+ markup = set_font("R");
+ if (current_size)
+ markup += "</FONT>";
+ current_size=nr;
+ if (nr)
+ {
+ markup += "<FONT SIZE=\"";
+ if (nr>0)
+ markup += '+';
+ else
+ {
+ markup += '-';
+ nr=-nr;
+ }
+ markup += char( nr + '0' );
+ markup += "\">";
+ }
+ markup += set_font( font );
+ return markup;
+}
+
+/* static int asint=0; */
+static int intresult=0;
+
+#define SKIPEOL while (*c && *c++!='\n')
+
+static bool skip_escape=false;
+static bool single_escape=false;
+
+static char *scan_escape_direct( char *c, QCString& cstr );
+
+/**
+ * scan a named character
+ * param c position
+*/
+static QCString scan_named_character( char*& c )
+{
+ QCString name;
+ if ( *c == '(' )
+ {
+ // \*(ab Name of two characters
+ if ( c[1] == escapesym )
+ {
+ QCString cstr;
+ c = scan_escape_direct( c+2, cstr );
+ // ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
+ name = cstr;
+ }
+ else
+ {
+ name+=c[1];
+ name+=c[2];
+ c+=3;
+ }
+ }
+ else if ( *c == '[' )
+ {
+ // \*[long_name] Long name
+ // Named character groff(7)
+ // We must find the ] to get a name
+ c++;
+ while ( *c && *c != ']' && *c != '\n' )
+ {
+ if ( *c == escapesym )
+ {
+ QCString cstr;
+ c = scan_escape_direct( c+1, cstr );
+ const int result = cstr.find(']');
+ if ( result == -1 )
+ name += cstr;
+ else
+ {
+ // Note: we drop the characters after the ]
+ name += cstr.left( result );
+ }
+ }
+ else
+ {
+ name+=*c;
+ c++;
+ }
+ }
+ if ( !*c || *c == '\n' )
+ {
+ kdDebug(7107) << "Found linefeed! Could not parse character name: " << name << endl;
+ return "";
+ }
+ c++;
+ }
+ else if ( *c =='C' || c[1]== '\'' )
+ {
+ // \C'name'
+ c+=2;
+ while ( *c && *c != '\'' && *c != '\n' )
+ {
+ if ( *c == escapesym )
+ {
+ QCString cstr;
+ c = scan_escape_direct( c+1, cstr );
+ const int result = cstr.find('\'');
+ if ( result == -1 )
+ name += cstr;
+ else
+ {
+ // Note: we drop the characters after the ]
+ name += cstr.left( result );
+ }
+ }
+ else
+ {
+ name+=*c;
+ c++;
+ }
+ }
+ if ( !*c || *c == '\n' )
+ {
+ kdDebug(7107) << "Found linefeed! Could not parse (\\C mode) character name: " << name << endl;
+ return "";
+ }
+ c++;
+ }
+ // Note: characters with a one character length name doe not exist, as they would collide with other escapes
+
+ // Now we have the name, let us find it between the string names
+ QMap<QCString,StringDefinition>::iterator it=s_characterDefinitionMap.find(name);
+ if (it==s_characterDefinitionMap.end())
+ {
+ kdDebug(7107) << "EXCEPTION: cannot find character with name: " << name << endl;
+ // No output, as an undefined string is empty by default
+ return "";
+ }
+ else
+ {
+ kdDebug(7107) << "Character with name: \"" << name << "\" => " << (*it).m_output << endl;
+ return (*it).m_output;
+ }
+}
+
+static QCString scan_named_string(char*& c)
+{
+ QCString name;
+ if ( *c == '(' )
+ {
+ // \*(ab Name of two characters
+ if ( c[1] == escapesym )
+ {
+ QCString cstr;
+ c = scan_escape_direct( c+2, cstr );
+ kdDebug(7107) << "\\(" << cstr << endl;
+ // ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
+ name = cstr;
+ }
+ else
+ {
+ name+=c[1];
+ name+=c[2];
+ c+=3;
+ }
+ }
+ else if ( *c == '[' )
+ {
+ // \*[long_name] Long name
+ // Named character groff(7)
+ // We must find the ] to get a name
+ c++;
+ while ( *c && *c != ']' && *c != '\n' )
+ {
+ if ( *c == escapesym )
+ {
+ QCString cstr;
+ c = scan_escape_direct( c+1, cstr );
+ const int result = cstr.find(']');
+ if ( result == -1 )
+ name += cstr;
+ else
+ {
+ // Note: we drop the characters after the ]
+ name += cstr.left( result );
+ }
+ }
+ else
+ {
+ name+=*c;
+ c++;
+ }
+ }
+ if ( !*c || *c == '\n' )
+ {
+ kdDebug(7107) << "Found linefeed! Could not parse string name: " << name << endl;
+ return "";
+ }
+ c++;
+ }
+ else
+ {
+ // \*a Name of one character
+ name+=*c;
+ c++;
+ }
+ // Now we have the name, let us find it between the string names
+ QMap<QCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
+ if (it==s_stringDefinitionMap.end())
+ {
+ kdDebug(7107) << "EXCEPTION: cannot find string with name: " << name << endl;
+ // No output, as an undefined string is empty by default
+ return "";
+ }
+ else
+ {
+ kdDebug(7107) << "String with name: \"" << name << "\" => " << (*it).m_output << endl;
+ return (*it).m_output;
+ }
+}
+
+static QCString scan_dollar_parameter(char*& c)
+{
+ unsigned int argno = 0; // No dollar argument number yet!
+ if ( *c == '0' )
+ {
+ //kdDebug(7107) << "$0" << endl;
+ c++;
+ return s_dollarZero;
+ }
+ else if ( *c >= '1' && *c <= '9' )
+ {
+ //kdDebug(7107) << "$ direct" << endl;
+ argno = ( *c - '0' );
+ c++;
+ }
+ else if ( *c == '(' )
+ {
+ //kdDebug(7107) << "$(" << endl;
+ if ( c[1] && c[2] && c[1] >= '0' && c[1] <= '9' && c[2] >= '0' && c[2] <= '9' )
+ {
+ argno = ( c[1] - '0' ) * 10 + ( c[2] - '0' );
+ c += 3;
+ }
+ else
+ {
+ if ( !c[1] )
+ c++;
+ else if ( !c[2] )
+ c+=2;
+ else
+ c += 3;
+ return "";
+ }
+ }
+ else if ( *c == '[' )
+ {
+ //kdDebug(7107) << "$[" << endl;
+ argno = 0;
+ c++;
+ while ( *c && *c>='0' && *c<='9' && *c!=']' )
+ {
+ argno *= 10;
+ argno += ( *c - '0' );
+ c++;
+ }
+ if ( *c != ']' )
+ {
+ return "";
+ }
+ c++;
+ }
+ else if ( ( *c == '*' ) || ( *c == '@' ) )
+ {
+ const bool quote = ( *c == '@' );
+ QValueList<char*>::const_iterator it = s_argumentList.begin();
+ QCString param;
+ bool space = false;
+ for ( ; it != s_argumentList.end(); ++it )
+ {
+ if (space)
+ param += " ";
+ if (quote)
+ param += '\"'; // Not as HTML, as it could be used by macros !
+ param += (*it);
+ if (quote)
+ param += '\"'; // Not as HTML, as it could be used by macros!
+ space = true;
+ }
+ c++;
+ return param;
+ }
+ else
+ {
+ kdDebug(7107) << "EXCEPTION: unknown parameter $" << *c << endl;
+ return "";
+ }
+ //kdDebug(7107) << "ARG $" << argno << endl;
+ if ( !s_argumentList.isEmpty() && argno > 0 )
+ {
+ //kdDebug(7107) << "ARG $" << argno << " OK!" << endl;
+ argno--;
+ if ( argno >= s_argumentList.size() )
+ {
+ kdDebug(7107) << "EXCEPTION: cannot find parameter $" << (argno+1) << endl;
+ return "";
+ }
+
+ return s_argumentList[argno];
+ }
+ return "";
+}
+
+/// return the value of read-only number registers
+static int read_only_number_register( const QCString& name )
+{
+ // Internal read-only variables
+ if ( name == ".$" )
+ {
+ kdDebug(7107) << "\\n[.$] == " << s_argumentList.size() << endl;
+ return s_argumentList.size();
+ }
+ else if ( name == ".g" )
+ return 0; // We are not groff(1)
+ else if ( name == ".s" )
+ return current_size;
+#if 0
+ // ### TODO: map the fonts to a number
+ else if ( name == ".f" )
+ return current_font;
+#endif
+ else if ( name == ".P" )
+ return 0; // We are not printing
+ else if ( name == ".A" )
+ return s_nroff;
+#ifndef SIMPLE_MAN2HTML
+ // Special KDE KIO man:
+ else if ( name == ".KDE_VERSION_MAJOR" )
+ return KDE_VERSION_MAJOR;
+ else if ( name == ".KDE_VERSION_MINOR" )
+ return KDE_VERSION_MINOR;
+ else if ( name == ".KDE_VERSION_RELEASE" )
+ return KDE_VERSION_RELEASE;
+ else if ( name == ".KDE_VERSION" )
+ return KDE_VERSION;
+#endif
+ // ### TODO: should .T be set to "html"? But we are not the HTML post-processor. :-(
+
+ // ### TODO: groff defines much more read-only number registers
+#ifndef SIMPLE_MAN2HTML
+ kdDebug(7107) << "EXCEPTION: unknown read-only number register: " << name << endl;
+#endif
+
+ return 0; // Undefined variable
+
+}
+
+/// get the value of a number register and auto-increment if asked
+static int scan_number_register( char*& c)
+{
+ int sign = 0; // Sign for auto-increment (if any)
+ switch (*c)
+ {
+ case '+': sign = 1; c++; break;
+ case '-': sign = -1; c++; break;
+ default: break;
+ }
+ QCString name;
+ if ( *c == '[' )
+ {
+ c++;
+ if ( *