p3scan-2.3.2/0000755000175000001440000000000010347310635011545 5ustar jlaiusersp3scan-2.3.2/NEWS0000644000175000001440000000022410347310635012242 0ustar jlaiusersUnless there is a bug fix release, the next release after 2.2 should be version 3.0 that includes GNU AutoMake tools support as the main objective. p3scan-2.3.2/p3scan-it.mail0000644000175000001440000000213310347310635014211 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Salve %USERNAME%. Il testo di questo messaggio stato generato automaticamente da P3Scan, che in esecuzione su %HOSTNAME%.%DOMAINNAME% per controllare la presenza di virus su tutta la posta in arrivo. Sostituisce il corpo del messaggio originario che risultato contenere un VIRUS (invece della email infetta ti stato recapitato questo messaggio). Gli header del messaggio originale sono stati lasciati invariati, quindi per avere informazioni complete sul messaggio originale puoi controllare gli header di questo. Ecco un rapporto sintetico sulla scansione (attenzione, tutti i virus recenti falsificano il mittente, che pertanto non assolutamente attendibile): Nome virus: %VIRUSNAME% Mittente: %MAILFROM% Destinatario: %MAILTO% Data del messaggio: %MAILDATE% Subject: %SUBJECT% Dati della connessione: %PROTOCOL% from %CLIENTIP%:%CLIENTPORT% to %SERVERIP%:%SERVERPORT% File col messaggio: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% by Jack S. Lai . p3scan-2.3.2/p3scan_readme.8.gz0000644000175000001440000001325610347310635014770 0ustar jlaiusersмzCp3scan_readme.8;isǕWt[1:⬍TKQ5,ٛJT\! i?zwS]29/GZZ.2]XALn*]Dm~_e:}DO|bN2TxSܛlc*zw _Ea>rXoo:jmҹ+ueZUx;SmQH׋yRYD )y&w͛G)ՃʖޤƩ3X5V&mGAX6F5roLmwj[Q6Mz'8S+EuGIKŷԀ:M[ZU.n`l+Ml3{3< @Sx|K 7,RW1u]l,1r~ɑZ{P 3_7R?UF*UʅN+R5靚!ىC)Puψ;ky@>, T!}S*A{uTrpvoz0iviljw*wz\]4[4C:UJ!͕uQ2$i+ d~0;\9E$dvԪ)"ҭD*}d8_D-TОwr(E38GrLEZl6&L@YQRKf-Ț9ˏpM^*5"YѓG*`R:3G{\lLM.,E+񲢲;B$&y@~BiA:W*ۓ;!V+S'c$.CG4B^8EL _ʤG !ں|ؠLhPoo]&*^o%7 -|GGۢE,Qᅚ-m2]HXdž6`'LJQ"PRĤTb}E$*ptȷ*qo IΰF%O@ XmR^v&<;MљLlmv['"T`lz5ni*@-#әmYآJL5 (,epCDԙ0(=z@ejqq+X|i7݄c_s3Aj:gg'ѯbY+JxYq7o68m]S1/rHT!">,Շ1]X~zOr{}˾u͌bF pf/tݘ{MV匤V+x 4=KD`WgE PP'%#4: gK+=ˋk{M8i|SFLHj Ƣ} Zu1"ޕX7DjA߯!ZS9Q^b {W d8pq<,Kq=Q9<_ήg7U_5[>U 8^OV7i2_[4R,1K)r:<</HLxk.8mAB7|/!L~G@RnVJ6@$nמ e)&}uςZ6hw$2_Mn'IP.ɲ:$r(a9?R:%79zbL6ɬ( Ar!" sR>`!6` ,-^D}"]Q$D;k0hm6~|HuGIR01u,18:ybz6R{]Mƣ4 +A{&1@L2L:Ej݋H70:d`(slaICoߪ]v(Y&m!@!:;VaL05k\nPK tp 䊗S$WBþ\3:+1#]a[B):!C+*;ԂlEpE' iHB"ıV ddN*KZ.m7RH1u&L(#aCet >.E}Kdp*= 1QPGr9yxy!9[@< fŋ'OkJ /$u$z2ݲ Wbe-{}py!#`3‡").Su!)SiRFWSlMpZ`GSaREs.$PX#OaSo3Zͺb0~B`LG=eǺdB{n V*88' f*5@t O A8kt{sxqO0OpcK>bTh&"b}/ןWkȁ KuLж4%rE)oz5BCpVW m. *)c'bITub_;peS{7UƩZ;4QK\,G Qw5!6r58@<1,S!_v"ýrD~"8&!:ĺeSa,=p'C[eH3͊6,{1Y[D&ͱY _&H[OLMȶx3OΧ4Z>Df تQxo;X9h;{<| w@͡6n[9[usAmޔ=j-xH ZJ̮I5Xt 'Z`ڸ . hAA ]t|AޫB@7Uq.H U#&cBRVW. o[ C+Bܹ,Y'0:F~qBzA2ЈPބQmT|0 ]Uܙv{jQX-_c !]N DçfPƻtugxRm2P0_-Z +->U'`L{o;[QӖ +P8Ao8u<WCalLLڑMu$,i:ⒺiJ\_Vd|,0`brp1s:m8Qo}^ hHր{Bƒ3PQώ)!N|$tyGӪR` ?@P`C]L~^a~m4G q 㛜|$IRR*xc` f~#$ۆ،To  =A?Nh\Wؐ(,OJb`Ö֒jL(p]{8"0>mƶ݅M0,ǯGcjJ;Dq3t~8`?ZwA |O:! MUTI' &h)kqz(@IVl`зI4vli&ЖF7\H`9<΀Ρ}_Z@5]!^#R+B?Y/X03ҐmSaC*DI9‹:?J*>?I9%`;Е#KnMOaSvTtUɹИm+^>N{Y^\Qbm\Z r,tUf&nC2t{""unrD:Kyk=E;O] S1!dsaǽҦ<@to'sFfŽymN]oۺAo j%%<)4E΄;R[Eg- jEgοΘ-=pa;ӿ o05B52uߟ\C?L(_B@lnCaNagM#E9+^F]l]u Z>sT~ߌs,RQX|F77pN^j$wXG=1Œ 4Fsغ/FRBܟbb@j`?JJQaQAD,WYሖ\< |&R# P{@2]K0Q&ǏB-U%3 Raey@@fa WZ<4Bdy]i184I/sK|ΉfP@9 DYgۨݏ?ןnUUN}\g+8p3scan-2.3.2/Makefile0000777000175000001440000000000010347310635016302 2Makefile-ripmimeustar jlaiusersp3scan-2.3.2/CONTRIBUTERS0000644000175000001440000000200010347310635013403 0ustar jlaiusersThe following people have contributed to p3scan in the form of code since it's implementation and I would like to thank them here on behalf of everyone using this program and especially, sincere thanks are given from me. Mahalo Nui Loa! Guillermo Borgobello Olivier Bourdon (Mataru) Gabriele Carioli R. Ghetta Christophe Grenier (cgsecurity) Carl Hughey (Selenetix - Information Technology) Hieu Nguyen (CADENA IT-Services) Luiz Henrique Ozak Philippe Paget (Mataru) Ramn Arnal Palas Rubn Daro Ponticelli Stphan Rafin Simon Santesteban Joe Wright ------------------------------------------------------------------------------ Hardware support has been provided by Industry Standard Computers http://www.ISCnetwork.com - Thank you Butch! Hosting support has been provided by Sourceforge.net http://sourceforge.net - Thank you! Of course thanks go out to all the people on the p3scan-main mailing list for the discussions driving this project and it's path. http://sourceforge.net/mailarchive/forum.php?forum_id=37097 p3scan-2.3.2/spamfaq.html0000644000175000001440000056652310347310635014104 0ustar jlaiusers alt.spam FAQ or "Figuring out fake E-Mail & Posts".

Jamie, in a kind inimitable way, has informed me that some of the scumware sites are showing this page in popups.  If you see this alt.spam FAQ in a popup please be assured that spyware / adware sites are doing this to try to discredit anti-spam / anti-spyware sites.  See:
http://www.spywareinfo.com/~merijn/downloads.html
 and
http://www.spywareinfo.com/articles/hijacked/

Please see my section on removing spyware.

Thanks,
Ken


From: gandalf@digital.net
Subject: alt.spam FAQ or "Figuring out fake E-Mail & Posts". Rev 20050130
Newsgroups: alt.2600, alt.spam, alt.newbie, news.admin.net-abuse.misc, news.admin.net-abuse.email, news.admin.net-abuse.usenet, alt.answers, news.answers
Followup-To: news.admin.net-abuse.misc, alt.spam, news.admin.net-abuse.usenet
Summary: This posting describes how to find out where a fake post or e-mail originated from.

Archive-name: net-abuse-faq/spam-faq
Posting-Frequency: monthly
Last-modified: 20050130
URL: http://gandalf.home.digital.net/spamfaq.html

Greetings and Salutations:

This FAQ will help in deciphering which machine a fake e-Mail or post came from, and who (generally or specifically) you should contact.

The three sections to this twelve portion FAQ (With apologies to Douglas Adams :-)) :
   o   Introduction
          o   The Easy Way To Get Rid Of spam
   o   Tracing an e-mail message
          o   What computer did this e-mail originate from?
          o   MAILING LIST messages
   o   Reporting Spam and tracing a posted message
   o   WWW IP Lookup URL's
   o   Converting that IP to a name
          o   What to do with "strange" looking Web links
          o   Getting a World Wide Web page busted
   o   Usenet complaint addresses
          o   Viruses / Trojans / Spyware
          o   Fraud on the Internet and The MMF (Make Money Fast) Posts
          o   Nigerian Advance Fee Fraud
          o   Hoaxes
          o   Open system spammers love
   o   Filtering E-Mail BlackMail, procmail or News with Gnus
          o   Rejecting E-Mail from domains that continue to Spam
   o   Misc. (Because I can't spell miscellaneous :-)) stuff
         I couldn't think to put anywhere else.
          o   Protection for you and your kids on the Internet
          o   I am interested in eliminating spam from my emails, how do I do this?
          o   Origins of Spam
          o   How *did* I get this unsolicited e-mail anyway?
          o   Can I find the persons name and phone from an e-mail address?
          o   How To Respond to Spam
          o   Firewalls and protecting your computer
   o   Revenge - What to do & not to do (mostly not)
          o   Telephoning someone
          o   Snail Mailing someone
   o   1-900, 1-800, 888, 877 and 1-### may be expensive long distance phone calls
   o   Junk Mail - The Law
   o   Additional Resources - Lots Of Links


    Introduction
=============

Jamie, in a kind inimitable way, has informed me that some of the scumware sites are showing this page in popups.  If you see this alt.spam FAQ in a popup please be assured that spyware / adware sites are doing this to try to discredit anti-spam / anti-spyware sites.  See:
http://www.spywareinfo.com/~merijn/downloads.html
 And
http://www.spywareinfo.com/articles/hijacked/

Also please see my expanded section on removing spyware.

Please feel free to repost this, e-mail it, put this FAQ on CD's or any other media you can think of.  Just please do not pop it up on the screen of anybody who didn't request it.

The latest & greatest version of the Spam FAQ is found at:
http://gandalf.home.digital.net/spamfaq.html
   or
http://home.digital.net/~gandalf/spamfaq.html
 or
 ftp://rtfm.mit.edu/pub/usenet/alt.spam/
Also see:
ftp://rtfm.mit.edu/pub/usenet/alt.syntax.tactical/

Please email follow-ups / additions / changes comments / questions to gandalf@digital.net . . . BUT PLEASE NOTE because I receive (on the average) over 200 e-mails EVERY day (of which 195 or so are spam) you MUST put the words "Alt.spam" in the subject of the e-mail or there is a VERY good chance the e-mail will be deleted without being read.  I get 10 or 15 "No Subject" spams a day.

My news source is OK, but I sometimes miss items.

I accept all and any input.  I consider myself to be the manager of this FAQ for the good of everyone, not the absolute & controlling Owner Of The FAQ.  I do not always write in a completely coherent manner.  What makes sense to me may not make sense to others.  If the community wants something added or deleted, I will do so.  I removed any e-mail and last name references to someone making a suggestion / addition.  This is so that someone doesn't get upset at this FAQ and do something stupid.  If you don't mind having your e-mail in this FAQ (or where it is required), please tell me and I will add it back in.

If you are in the United States and have not yet written to your Senator or House of Representatives about how terrible the CAN-SPAM act is, I would ask you to do so.  Bottom line is that there are many large corporations and over 22.9 million small businesses on the United States.  If you received just one e-mail a year from each of the small businesses (I am not even including large companies) you would receive 63,800 e-mails PER DAY.  According to CAN-SPAM you would then be required to opt out of each and every one of these e-mails, and the company has 10 days to honor your request.  Of course this would not stop spammers from changing company names every 10 days and just start spamming all over again.  I have written a letter explaining why I think that this act was poorly written, and I would ask you to write a letter to your representatives also:
http://home.digital.net/~gandalf/CAN-SPAM.htm
http://gandalf.home.digital.net/CAN-SPAM.htm

How did this incredibly bad law get passed?  This law was written without any public hearings, with input from only the marketing industry and Internet Service Provider lobbies (guess who loses, You Do).  From http://www.cauce.org/news/index.shtml :
"CAUCE is also disappointed that both the House and Senate versions of this law were passed without any public hearings, instead being written and passed solely through back-room compromises and with the input of the marketing industry and Internet Service Provider lobbies, but with scant regard for the interests of America's consumers and business Internet users."

Apparently one of the lobbying groups talking to our representatives (for you) is The Center for Democracy and Technology.  They were kind enough to speak for "everybody" in this missive sent to Congress:
http://www.cdt.org/speech/spam/031015cdt.shtml

They supported everything the Direct Marketing Association ( http://www.the-dma.org/ (telemarketers)) and spammers wanted in a bill and more.

CDT is supported by many different companies:
http://www.cdt.org/mission/supporters.shtml

Find Your Senators at http://www.senate.gov/general/contact_information/senators_cfm.cfm and find your US Representative: http://www.house.gov/writerep/ (Fill in your state and zip, click "Contact My Representative" and you will be told who your representative is). Go To: http://www.house.gov/house/MemberWWW.html , click on their site and your representative should have an address at the bottom of the page for where to write them.  I would also suggest that you cc the two sponsors of the bill: Conrad Burns 187 DIRKSEN SENATE OFFICE BUILDING WASHINGTON DC 20510 and Ron Wyden 516 HART SENATE OFFICE BUILDING WASHINGTON DC 20510.

Davjohn suggests going to http://congress.org , plug in your zip code and click on GO.  Internet Explorer and Netscape will show you your representatives.  Safari browser did not work at this site.

And why CAN-SPAM won't work:
http://www.google.com/search?q=CAN-SPAM+won%27t+work
http://www.google.com/search?q=Critics+CAN-SPAM
http://www.gripe2ed.com/scoop/story/2003/12/11/9145/0712
http://www.circleid.com/article/725_0_1_0_C - And how the DMA is trying to convince the public that CAN-SPAM works

Before trying to determine where the post or e-mail originated from, you should realize that (just like The National Enquirer http://www.nationalenquirer.com/ or a logical argument from Canter and Siegel) the message will have *some* amount of truth, but all or most of the information may be forged.  Be careful before accusing someone.

Commands used in this FAQ are UNIX & VMS commands.  Sorry if they don't work for you, you might wish to try looking around at your commands to find an equivalent command (or I might be able to help out some).  There are programs for the Macintosh and Windows machines that do the same thing the UNIX commands do, see the above URL's for where to locate this software.

And no, I am not going to tell you how to post a fake message or fake e-mail.  It only took me about 2 days (a few hours a day) to figure it out.  It ain't difficult.  RTFM (or more appropriately, Read The @&%^@# RFC).

Every e-mail or post will have a point at which it was injected into the information stream.  E-mail will have a real computer from which it was passed along.  Likewise a post will have a news server that started passing the post.  You need to get cooperation of the postmaster at the sites the message passed thru.  Then you can get information from the logs telling you what sites the message actually passed thru, and where the message "looked" like it passed thru (but actually didn't).  Of course you do have to have the cooperation of all the postmasters in a string of sites...

The Easy Way To Get Rid Of spam
=========================

Sorry to tell you this but if you received a spam (Unsolicited Commercial E-Mail) there is no "easy" way to get the spam stopped.  Generally if you reply (unsubscribe) this confirms that your e-mail address is "live" and just gets your e-mail address sold to other spammers.  Spam has to be dealt with one at a time.  Sorry, it isn't easy to stop the spam.  The "Internet" (the collective non-profit and profit entities of the network) is trying to fix this problem but it is taking time.  The "easiest" way to stop getting spam is to change your e-mail address and only give your e-mail address to people you absolutely trust, and to NEVER allow the e-mail address to be posted to a web site or posted ANYWHERE on the internet.  To see how many times my e-mail address appears on the Internet go to the following link:
http://www.google.com/search?q=gandalf%40digital.net
http://www.nwfusion.com/newsletters/edu/2003/0324ed1.html - E-Mail addresses on the web attract the most spam

It your e-mail address shows up on a search engine, then the spammers can find your e-mail address also.  Be careful about giving your e-mail address to companies that purport to be against spam:
http://www.gripe2ed.com/scoop/story/2003/5/15/10299/0559

There are businesses that make a good living filtering out spam both on a personal and corporate level.  I would suggest that if you really don't want to deal with spam that you get an e-mail address from one of these services (Please note I am not recommending this service, just using it as an example).  Do a search:
http://www.google.com/search?q=email+hosting+spam
And you will come up with companies like:
http://www.No-JunkMail.com/

Or if you wish to block it from your personal e-mail account do a search on something like:
http://www.google.com/search?q=spam+blocking+software
And you will come up with examples like:
http://www.spamulor.net/ - Free
http://www.spambutcher.com/

Be aware that no spam blocking software (as of yet) is perfect and you may get "false positives".  An e-mail from a friend may be detected as spam and may get deleted as spam or moved to the spam box.  The spam wars:
http://computerworld.com/softwaretopics/software/groupware/story/0,10801,75737,00.html

Davjohn reminds us that if you are required to give a "legal" e-mail address to a company you don't know or trust, go to http://mail.com and set up a free account. There are a hundred-or-so variations available. General.delivery@arcticmail.com sounds like a Santa Clause e-mail address.  He has 2 addresses there. About once a week he goes in and clicks "empty" and ~flush~ it's all gone.

 
        Tracing an e-mail message
============================================

To trace the e-mail you have to look at the header.  Most mail readers do not show the header because it contains information that is for computer to computer routing.  The information you usually see from the header is the subject, date and the "From" / "Return" address.  About the only thing in an e-mail header that can't be faked is the "Received" portion referencing your computer (the last received).

You will need to take a look at the headers on the message as follows (Thanks to Bob, Dave, Kathy, Michael, Piers, Russ, Simon, Chalmers and others) :
Claris E-Mailer - under Mail select Show Long Headers.
Eudora (before ver. 3) - Select Tools , Options... , then Fonts & Display then Show all headers
Eudora (ver. 3.x, 4.x IBM or Macintosh) - Press the BLAH button on the incoming mail message
Eudora V5.1:
       1) Double-click on the email subject line in the current mailbox. This displays the same message with a fuller version of the header, which will be enough for some ISPs but not all, and also shows an extra Toolbox which contains the BlahBlahBlah button
       2) Click on the BlahBlahBlah button
For Mac Eudora 4.x, hitting the following will cause Eudora to alter its default setting so that BLAH will be automatically selected for all new email received after this switch is set:
<x-eudora-setting:123=y> When checked, Eudora will show all the headers from messages, not just an abbreviated set.
Hotmail - How to set show the mail headers in hotmail:
1.  After you login, just to the right of the tabs, select Options
2.  Under Additional Options, select Mail Display Settings
3.  In the Message Headers section, click the Advanced button
JUNO - Click on the word "OPTIONS" in the MENU BAR.
On This menu, click on "E-Mail Options (ctrl-E)"
This will get you a Dialog Box:
In the "Show message headers" part, you need to have the "Full" button marked in order to show full message headings.
KMAIL (KDE Mail Client) - Bryan tells us To display all headers in kmail(KDE mail client), go to 'view' and click 'all headers'.
Lotus Notes R4 and R5:
1) Examine the fields in the document.
   Click on File --> Document Properties
   Click on fields tab (square rule)
   Scroll down to the "received" fields - there should be one for each "received" header added.
   Copy and paste these into a file.
2) Export the headers from the document
   *important*  You need to be in the inbox folder in Notes
   Select the document.
   Click on File --> Export
   Enter a temporary file name, ensure File type is "Structured Text"
   Under Export options, click on "selected documents", click OK.
   The generated file contains all the headers on the message along with the message body.
Lotus Notes R6: Open the mail, View/ Show /Page Source and the OpenNTF mailtemplate has an action to forward the full header (to yourself, or to support for instance).  You may want to copy that, or use the template.
MS Outlook - Double click on the email in your inbox. This will bring the message into a window. Click on View - Options.  You can also open a message then choose File....Properties....Details.
Microsoft Outlook 2000 - From the Menu Bar select "View" and then "Options" from that menu.
This displays a dialogue box called "Message Options".
The largest and last text box is called "Internet headers:"
Scroll through this to read all the details.
To save a copy, highlight all the content, and copy it to the clipboard by pressing <Ctrl C> (thats both those keys at the same time), then go into whatever word processor or email program you wish and press <Ctrl V> to paste the text onto that page.

Because Microsoft Outlook has many security flaws, the below instructions may expose your computer to risks.  See:
http://www.the-foxhole.org/Disabling_IE_Security_Flaws.htm
MS Outlook Express - Alt-Enter, or Alt-F then R.
MS Outlook Express - More Detailed:
  To look for, copy and send headers In Outlook Express
  1- Press CTRL F3
  2- Press CTRL A
  3- Press CTRL C
  4- Press Alt F4. (At this point the message is already copied)
  5- Open a new message. Right click and paste or select Edit and paste.

Mike tells us a better way to expose the headers and copy the body for MS Outlook Express is as follows:
http://www.spamcop.net/fom-serve/cache/119.html
The mouse selections are File/ Properties/ Details tab/ Message source button.  The keyboard access is alt-Enter ctrl-Tab alt-M.  Once accessed the remainder of commands are as discussed elsewhere:  Mouse; R click context menu, Select all, Copy or Keyboard;  ctrl-A ctrl-C.  The Message Source described here is the headers + attached spam body.  If one only wanted the complete headers without spam body, they would stop one step earlier at the Details tab section above.

Netscape 3 - In the mail viewing window: Options > Show Headers > All - When all the headers are displayed in the NS3 mail window, they are formatted. This is much more readable than the display in a text editor such as Notepad.
Netscape 4.xx - Double click on the email in your inbox. Click on View - Headers - All.
PINE - You have to turn on the header option in setup, then just hit "h" to get headers.
WebTV - http://www.haltabuse.org/help/headers/webtv.shtml :
   1)  While viewing the email, hit "Forward" on the sidebar. Address the document to yourself. Completely erase the subject line.
   2)  Put your cursor on the first line of the "body" (text area); Hit "Return" (enter) twice. Your cursor should now be on the 3rd line of the text area.
   3)  Type any "Alt" character on this line; DO NOT HIT "RETURN"
   4)  Cut and Paste the "Alt" character onto the subject line: (CMD+"A"), (CMD+"X"), (CMD +"V") The "Alt" character should "jump" down to the message text-area.
   5) Hit "Send"; open the received mail.
Ximian Evolution (Linux email program) to display full headers, open the message, go to the VIEW menu and choose message display>full headers.
Yahoo-
-Click on the "Mail Options" link located near the top right-hand side of the page.
-Click the "General Preferences" link.
-Locate the Show Headers heading and select either "Brief" or "All."
-Click the "Save" button to put your new settings into effect.
Another way to show you how to display headers, please see (with some good screen shots):
http://help.att.net/docs/use/email/gen/prb_msol_mac_headerinfo.htm?platform=osnone - MS Outlook Express for the Mac
http://help.att.net/docs/howto/other/win/prb_all_all_ns-header.htm?platform=osnone - Netscape Messenger or Netscape Mail
http://www.wurd.com/cl_email_outlook_headers.php - MS Outlook
http://www.wurd.com/cl_email_msie_headers.php - MS Outlook Express

Programs that do not comply with any Internet standards (like cc-Mail (depending on how it is configured), Beyond Mail, VAX VMS) throw away the headers.  You will not be able to get headers from these e-mail messages.

George tell us that the gateway that Lotus provides, SMTPLink (is one of those Microsoft-style utilities that's functional, but just barely) has an administrator-configurable setting for handling RFC-822 headers on inbound (to cc:Mail) messages.  Headers can be completely discarded, or copied to an attachment.

George also tells us in the R6 client, headers (if saved to an attachment in the gateway) are viewable as an attachment, as noted above.  The R8 client handles things differently, hiding the existence of the headers attachment, and making the content available only by going to the inbox or a message folder, right-clicking on "Properties", then selecting the "history" tab.  From there, it's possible to copy/paste into another document.  Header information is left in its original chronological order (unlike Notes, which takes the liberty of sorting all the headers into alphabetic order).

Aussie tells us that in Pegasus to view the full headers for each message, use CTRL-H. This will show the full headers for the particular message, but will not add them to any reply or forward. You need to cut/paste the message into the reply/forward to send these headers.

Richard tells us with Nettamer, a MS DOS based email and USENET group reader you must save the message as an ASCII file, then the full header will be displayed when you open the saved file with your favorite ASCII editor.

At this point if you are "pushing the envelope" on your ability to figure out how to get that complaint to the correct person, I would suggest joining the Usenet group alt.spam or news.admin.net-abuse.email and post the message with a title like "Please help me decipher this header".  Unfortunately there is no "single" place to complain to about spam (or Unsolicited Commercial E-Mail).  Complaints have to be directed to the correct ISP (Internet Service Provider) that the spam originated from.  See the below section entitled "Reporting spam".

URL's to help you figure out how to look at the headers:
http://support.xo.com/abuse/guide/guide1.shtml
http://www.rahul.net/falk/mailtrack.html

A little different description of headers:
http://digital.net/~gandalf/trachead.html - Line by line tracing of a spammers e-mail
http://digital.net/~gandalf/trachead2.html - Line by line tracing of a spammers e-mail when the spammer has inserted a "Fake" Received line to confuse tracking the e-mail.
http://help.mindspring.com/docs/006/emailheaders/
http://help.mindspring.com/features/emailheaders/extended.htm
 http://www.stopspam.org/email/headers/headers.html - In depth header analysis

There is spamming software that sends the e-mail directly to your computer.  This makes only one received line in the e-mail making your life many times easier.  The computer that is not your computer is the spamming computer.

Also, please look through the body of the message for e-mail addresses to reply to.  Complain to the postmasters of those sites also (see below for a list of complaint addresses).

Gregory tells us that assuming a reasonably standard and recent sendmail setup, a Received line that looks like :

Received: from host1 (host2 [ww.xx.yy.zz]) by host3
        (8.7.5/8.7.3) with SMTP id MAA04298; Thu, 18 Jul 1996 12:18:06 -0600

shows four pieces of useful information (reading from back to front, in order of decreasing reliability):
 - The host that added the Received line (host3)
 - The IP address of the incoming SMTP connection (ww.xx.yy.zz)
 - The reverse-DNS lookup of that IP address (host2)
 - The name the sender used in the SMTP HELO command when they
   connected (host1).

Looking at the below we see 6 received lines.  Received lines are like links in a chain.  The message is passed from one computer to the next with no breaks in the chain.  The received lines indicate that it ended up at digital.net (my computer) from mail.bestnetpc.com.  It was received at mail.bestnetpc.com from unknown (HELO paul-s.-aiello) ([205.160.183.123]).  The last three lines suggests that it was received at in2.|bm.net from mh.tomsurl|.com and from reb50.rs41|1date.net.  Since none of these computers are in the first two received lines then we can ignore these lines and every received entry after this line (this UCE had 4 or 5 more faked Received lines in it that were deleted for this example).  We also know that these lines are faked because no domain name has a "|" character in the name.  Domain names only have alphabetic or numeric characters in the name.

Do not get confused by the "Received: from unknown" portion.  The word "unknown" can be *anything* and should be ignored, this is whatever the spammer put in the SMTP HELO command when they connected to the SMTP server.

Received: from mail.bestnetpc.com (IDENT:qmailr@mail.bestnetpc.com [205.160.183.3]) by digital.net (8.9.1a/8.9.1) with SMTP id CAA10768 for <gandalf@digital.net>; Thu, 26 Nov 1998 02:55:11 -0500 (EST)
Received: (qmail 25259 invoked from network); 26 Nov 1998 08:05:49 -0000
Received: from unknown (HELO paul-s.-aiello) ([205.160.183.123])  by mail.bestnetpc.com with SMTP; 26 Nov 1998 08:05:49 -0000
Received: (from uudp@lcl|lhost) by in2.|bm.net (8.6.9/8.6.9) id CFF569794 for <suppressed>; Thursday, November 26, 1998
Received: from tomsurl|.com (mh.tomsurl|.com [100.257.57.69]) by m4.tomsurl|.com (8.6.12/8.6.12) with ESMTP id PAA21932 Thursday, November 26, 1998
Received: from reb50.rs41|1date.net (root@reb50.rs41|1date.net [256.36.1.176]) by tomsurl|.com (8.6.12/8.6.12) with ESMTP id PBA023891 for <suppressed>;

So we complain to whomever owns unknown (HELO paul-s.-aiello) ([205.160.183.123]).  Make sure that you do a nslookup (or use http://samspade.org/ , put the address in the section "address digger", click on WhoIs IP block and Traceroute and click on "do stuff") on the IP address's.  I try to verify 205.160.183.123 is paul-s.-aiello.  Indeed paul-s.-aiello does not even exist and 205.160.183.123 does not resolve to a name when I do a NSLookup.  Next would be a traceroute.  See further below for more in-depth tracking on resolving an IP.

IP portion = 205.160.183.123

Traceroute 205.160.183.123 gives us:
Step  Host                          IP
Find route from: 0.0.0.0 to: 205.160.183.123 (205.160.183.123), Max 30 hops, 40 byte packets
<snip>
13 acsi-sw-gw.customer.alter.net.   (157.130.128.26 ):   235ms
14 atlant-ga-2.espire.net.          (206.222.97.24  ):   272ms
15 206.222.104.37                   (206.222.104.37 ):   279ms
16 orland-fl-1-a5-0.espire.net.     (206.222.99.7   ):   362ms
17 iag.net.orland-fl-1.espire.net.  (206.222.106.6  ):   195ms
18 d1.s0.gw.dayb.fl.iag.net.        (207.30.70.38   ):   230ms
19 s0.gw.bestnetpc.net.             (207.30.70.254  ):   231ms
20 *     *     *
21 205.160.183.123                  (205.160.183.123):   372ms

See the traceroute section below for how to interpret the "*" (and other codes) that are returned from a traceroute.

Note - if you see something like the following realize that the only portion you can trust is within the "([" and the "])".  The spammer put in the (faked) portion "mail.zebra.net (209.12.13.2)" :
Received: from mail.zebra.net (209.12.13.2) ([209.12.69.42])

Kamiel tells us that you might also want to make sure that the IP is not hosted by an intermediary site.  Check it out at:
http://www.arin.net/

You should complain to the abuse@ or postmaster@<Last Two or Three words at the end of the name>.  I would complain to abuse@iag.net OR abuse@espire.net (but NOT both sites) since after looking below at the list of complaint addresses in this FAQ there are no alternate addresses for iag.net or espire.net.  Unless it is a "major provider" (someone in the below complaint list) I usually complain to the upstream provider rather than risk the chance of complaining to the spammer and being ignored.  If you go too far up the chain, however, it may take quite some time for the complaint to filter down to the correct person.

Louise tells us that you are entitled to make an 'alleged' accusation but to prevent yourself from being libel, prefix your statement with:-
"Without prejudice: I suspect you are the culprit of such and such."

The constitutional and legal boundary of 'Without prejudice' exempts Politician's opinions being spoken publicly and this prefix is often adopted by Solicitors (English) or Lawyers/Attorneys (USA).

I use :
abuse@XXXXX - Without prejudice I submit to you this Unsolicited Commercial E-Mail is from your user XXXX.  UCE is unappreciated because it costs my provider (and ultimately myself) money to process just like an unsolicited FAX.  Please look into this.  Thank you.

BE SURE to verify the IP address.  Windows '95 machines place the name of the machine as the "name" and place the real IP address after the name, meaning a spammer can give a legitimate "name" of someone else to get someone innocent in trouble.  A spammer at cyberpromo changed their SMTP HELO so that it claimed to be from Compuserve.  The Received line looked like the below, but a quick verification of the IP address 208.9.65.20 showed it was indeed from cyberpromo :

Received: from dub-img-4.compuserve.com (cyberpromo.com [208.9.65.20]) by karpes.stu.rpi.edu

The below e-mail was passed to me thru a "mule" (un1.satlink.com [200.9.212.3]).  The Spammer hijacked an open SMTP port to reroute e-mail to me:
Received: from un1.satlink.com (un1.satlink.com [200.9.212.3]) by digital.net (8.9.1a/8.9.1) with ESMTP id GAA06372; Fri, 27 Nov 1998 06:53:20 -0500 (EST)
Received: from usa.net ([209.86.128.234]) by un1.satlink.com (Netscape Messaging Server 3.54)  with SMTP id AAT2FEA; Fri, 27 Nov 1998 08:46:07 -0200

A NSLookup on 209.86.128.234 resolves to user38ld07a.dialup.mindspring.com, so after I complain to mindspring.com I also send the postmaster of the open SMTP port the following :
postmaster@XXXXX - Your SMTP mail server XXXXX was used as a mule to pass (and waste your system resources) this e-mail on to me.  You can stop your SMTP port from allowing rerouting of e-mail back outside of your domain if you wish to.  FYI only.  Info on how to block your server, see:
http://www.ordb.org/
http://dsbl.org/main
http://relays.osirusoft.com/
http://relays.osirusoft.com/cgi-bin/rbcheck.cgi - See if a server is on a BlackHole list, i.e. an open relay
http://www.dorkslayers.com/
http://spamhaus.org/sbl
http://mail-abuse.org/rbl/usage.html
http://samspade.org/
http://www.abuse.net/relay.html -  Test for server vulnerability

Now that Cable Modems are so popular, companies are starting to put their "personal" e-mail servers on cable / DSL modems and are (of course) not configuring them correctly.  I received UCE from an open SMTP server:
Received: from SDMAIN (DT1-A-hfc-0251-d1132e93.rdc1.sdca.coxatwork.com [209.19.46.147])                  by digital.net (8.9.3/05.21.76) with SMTP id SAA04761;            Fri, 30 Mar 2001 18:35:24 -0500 (EST)
Received: from Received: (qmail 554 invoked from network); 25 Mar 2001 23:56:02                  (ip207.miami41.fl.pub-ip.psi.net [38.37.111.207])        by SDMAIN; Fri, 30 Mar 2001 10:19:58 -0800

Complain to Cox ( abuse@home.com in this case) about their open SMTP server.

There are some systems that "claim" to "cloak" e-mail.  It is not true.  If you receive one that looks like the following :

Received: from relay4.ispam.net (root@[207.124.161.39]) by digital.net (8.8.5/8.8.5) with ESMTP id KAA28969 for <gandalf@digital.net>; Thu, 26 Jun 1997 10:41:46 -0400 (EDT)
Received: from --- CLOAKED! ---
      or
Received: from cerberus.njsmu.com ([204.142.120.2]) by digital.net (8.8.5/8.8.5) with ESMTP id HAA06250 for <gandalf@digital.net>; Mon, 25 Jan 1999 07:11:18 -0500 (EST)
From: hostme39@aol.com
Received: from The.sender.of.this.untracable.email.used.MAILGOD.by.IMI

It is still broken down as follows :
 - The route the e-mail took originated from one of the systems above the line marked "cloaked" or the line "untraceable" (in fact this makes it even easier to trace).  There is no magic to it.  Complain to that provider.  If you get no response from the site that spammed, you should ask your provider to no longer allow the above site [207.124.161.39] to connect to your system.

It has been kindly pointed out to me that there is a "feature" (read "bug") in the UNIX mail spool wherein the person e-mailing you a message can append a "message" (with the headers) to the end of their message.  It makes the mail reader think you have 2 messages when the joker that sent the original message only sent one message (with a fake message appended).  If the headers look *really* screwy, you might look at the message before the screwy message and consider if it may not be a "joke" message.

There are also IBM mainframes and misconfigured Sun Sendmail machines (SMI-8.6/SMI-SVR4) that do not include the machine that they received the SMTP traffic from.  You have to route the message (with headers) back to the postmaster at that system and ask them to tell you what the IP of the machine is that hooked into their system for that message.

An example of a Microsoft Exchange server that the "HELO" transaction is taken as the "From" portion (and is completely false) :
Received: from dpi.dpi-conseil.fr (dpi.dpi-conseil.fr [195.115.136.1])      by digital.net (8.9.3/8.9.3) with ESMTP id KAA06614        for <gandalf@digital.net>; Thu, 26 Aug 1999 10:51:31 -0400 (EDT)
Received: from FIREWALL ([192.168.0.254]) by dpi.dpi-conseil.fr with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2448.0)     id QW11TJV1; Thu, 26 Aug 1999 16:44:38 +0200

It has also been pointed out that someone on your server can telnet back to the mail port and send you mail.  This also makes the forgery virtually untraceable by you, but as always your admin should be able to catch the telnet back to the server.  If they telnet to a foreign SMTP server and then use the "name" of a user on that system, it may appear to you that the message came from that user.  Be very careful when making assumptions about where the e-mail came from.

Note for AOL users when looking at headers:
If you get double headers at the end of a message (like the below) the spammer has tacked on a extra set of headers to confuse the issue.  Ignore everything except the last set of headers.  These are the *real* headers.

------------------ Headers --------------------------------
Return-Path: <Gloria@me.net>
Received: from  rly-za05.mx.aol.com (rly-za05.mail.aol.com [172.31.36.101]) byair-za04.mail.aol.com (v51.16) with SMTP; Mon, 16 Nov 1998 19:16:02 1900
Received: from mailb.telia.com (mailb.telia.com [194.22.194.6]) by rly-za05.mx.aol.com (8.8.8/8.8.5/AOL-4.0.0) with ESMTP id TAA05189;
Mon, 16 Nov 1998 19:15:53 -0500 (EST)
From: Gloria@me.net
Received: from signal.dk ([194.255.7.40]) by mailb.telia.com (8.8.8/8.8.8) with SMTP id BAA14174; Tue, 17 Nov 1998 01:15:50 +0100 (CET)
Received: from 194.255.7.40 by signal.dk viaSMTP(950413.SGI.8.6.12/940406.SGI.AUTO) id AAA28586; Tue, 17 Nov 1998 00:53:13 +0100
Message-Id: <199811162353.AAA28586@signal.dk>
Date: Mon, 16 Nov 98 18:27:19 EST
To: Gloria@papa.fujisankei-g.com.jp
Subject: ATTENTION SMOKERS - QUIT SMOKING IN JUST 7 DAYS
Reply-To: Gloria@papa.fujisankei-g.com.jp

------------------- Headers --------------------------------
Return-Path: <lifeplanner@zcities.com>
Received: from  rly-yd04.mx.aol.com (rly-yd04.mail.aol.com [172.18.150.4]) by air-yd02.mx.aol.com (v56.14) with SMTP; Mon, 11 Jan 1999 23:54:48 -0500
Received: from phone.net ([207.18.137.42])
                    by rly-yd04.mx.aol.com (8.8.8/8.8.5/AOL-4.0.0)
                    with SMTP id XAA01327;
                    Mon, 11 Jan 1999 23:51:03 -0500 (EST)
From: <lifeplanner@zcities.com>
To: <Someone@aol.com>
Date: Tue, 15 Dec 1998 20:54:19 -0600
Message-ID: <13653344018870252@phone.net>
Subject: Life insurance, do you have it?
Mime-Version: 1.0
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable


  What computer did this e-mail originate from?
==================================================

You cannot generally tell by a e-mail header which specific computer the e-mail came from.  Just about every time you dial into your ISP (Internet Service Provider) you are assigned a different IP address.  If someone sends you an e-mail and they log out, the next time they log in their IP address will most likely be different.  If the computer has a permanently assigned IP address *and* you have the cooperation of whomever owns that block of IP addresses you *might* be able to get information on who might have sent the e-mail.

About the only way to tell *exactly* which e-mail account the e-mail was sent from is to get the ISP (Internet Service Provider) to tell you.  Usually the ISP will require you to get the local police involved (a warrant of some type) to force the ISP to give you that information.  Even given that you know the account the e-mail originated from, a forger can find out that person's account / password and log in as them, they can gain access to that computer while the person who owns that computer is away from the computer or they could install a back door program that allows them to control that person's computer remotely.  If this were to happen then the forger could send the e-mail and nobody would know who *specifically* sent the e-mail.


        MAILING LIST messages
========================================

Stephanie kindly defines MAILING LIST versus LISTSERVER :

A MAILING LIST is a type of email distribution in which email is sent to a fixed site which holds a list of email recipients and mail is distributed to those recipients automatically (or through a moderator).

A LISTSERVER is a software program designed to manage one or more mailing lists.  One of the more popular packages is named "LISTSERV".  Besides Listserv, other popular packages include Listproc which is a Unix Listserv clone (Listservs originated on BITNET), Majordomo and Mailserve.  Most importantly -- not all mailing lists run on listservers, there are many mailing lists that are manually managed.

You may hear of mailing lists being referred to as many things, some strange, some which on the surface make sense, like "email discussion groups".  But this isn't accurate either, since not all mailing lists are set up for discussion.

Istvan suggests "Majordomo software is remarkably funny about headers.  It does not like headers which contain anything odd. All messages the software receives which do not conform to its rigorous standards are simply forwarded to the list moderator.  It turns out this feature is effective at stopping between 80 and 90% of spam actually getting to the list."

Kirk tells us that you can set majordomo up so that new subscribers have to reply to a subscribe request, thus verifying the address is legit.  Additionally the lists can be configured so that only subscribers can post.  And finally you can put filters on content.  I've got the list I manage configured to reject multipart email and email which contains html.

Jeff adds that this would be the closed+confirm option in the configuration file so that only subscribers can post. Also, to prevent multipart or HTML this would be the taboo_headers configuration.

Richard mentions "Listserv can be configured to restrict non-members from sending to a list and can restrict spam based on the headers similar to Majordomo.  I've used both of these features successfully.  You can read more about Listserv capabilities, if you are interested, at:
     http://www.lsoft.com/listserv.stm
     http://www.lsoft.com/spamorama.html - FILTER (info on its spam filter)
I suspect that Listserv's spam filter may be better than Majordomo's (but I've not managed any Majordomo lists)."

Jeff adds that having ran a majordomo list for almost 4 years, I find majordomo to be every bit as good.  I should, however, qualify that; the listowner needs to have his/her clueons in good working order.  Simply put, no listowner in their right mind should leave their majordomo lists set to anything other than closed+confirm.  Alas, there are listowners who will leave their lists wide open.  I've also seen others knock themselves dead creating their own filters just so a listmember can post to the list from a web-based e-mail account while on vacation.  I usually tell anyone in such a situation to subscribe to the list from whatever free e-mail account they plan to use.  IMO, I cannot justify compromising list security for such reasons.  Lists should be closed+confirm...plain and simple.

Example Header appears below:
Received: from dir.bham.ac.uk (dir.bham.ac.uk [147.188.128.25]) by gol1.gol.com (8.7.5/8.6.9) with SMTP id GAA27292 for <XXXX@gol.com>; Sun, 5 May 1996 06:31:15 +0900 (JST)
Received: from bham.ac.uk by dir.bham.ac.uk with SMTP (PP) using DNS  id <26706-38@dir.bham.ac.uk>; Sat, 4 May 1996 20:56:49 +0100
Received: from emout09.mail.aol.com (actually emout09.mx.aol.com) by bham.ac.uk  with SMTP (PP); Sat, 4 May 1996 21:13:03 +0100
Received: by emout09.mail.aol.com (8.6.12/8.6.12) id PAA29156; Sat, 4 May 1996 15:35:53 -0400
Date: Sat, 4 May 1996 15:35:53 -0400
From: Jeanchev@aol.com
Message-ID: <960504153553_287142426@emout09.mail.aol.com>
Subject: CRaZy Complimentary Offer........

This is a post from Kevin Lipsitz for his "===>> FREE 1 yr. USA Magazine Subscriptions". The latest information indicates that the state of New York has told him he should stop abusing the Internet for a while ... lets hope it is forever.  In relation to the Internet he makes a slimy used car salesman look like a saint.

But as David reminds us, There are a million Kevin J. Lipsitz's out there.  All selling magazines, Amway, vitamins, phone service, etc.  All the losers who want to get rich quick, but can't start their own business.
 
That having been said, e-mail from a Listserve can usually be broken down the same way as "normal" e-mail headers.  There are just more waypoints along the way.  As you can see from the above, the e-mail originated from :

emout09.mail.aol.com

Jeff also mentions that news.admin.net.abuse.e-mail is a good newsgroup to monitor about how to keep spam off the listserve.  I have seen mailing list issues arise occasionally.

  Reporting Spam and tracing a posted message
============================================
If someone posts a message with your e-mail in the From: or Reply-To: field, it can (and will if you request) be canceled.  Please repost the message to news.admin.net-abuse.misc WITH THE HEADERS (or it will probably be ignored) so that the message cam be canceled (the message-id is the most important) with a suggested subject of the following:

Subject: FORGERY <Subject from the Spam message>

Or you can look at the Cancel FAQ at :
http://www.killfile.org/faqs/cancel.html

Try to make sure that the message has not already been posted to news.admin.net-abuse.misc, news.admin.net-abuse.email or news.admin.net-abuse.usenet and that it is less than 4 or 5 days old.  Chris reminds us that yes, there are a lot of annoying, off-topic and stupid postings out there.  But that doesn't make it spam.  _Really_.  All we're concerned with is _volume_.   Don't report any potential spams unless you see at least two copies in at least 4 groups.  The content is irrelevant.  Spam canceling cannot be by content.

For off topic posts, see http://digital.net/~gandalf/trollfaq.html

The first thing to do is to post the ENTIRE message (PLEASE put the header in or it will probably be ignored) to the newsgroup news.admin.net-abuse.misc.  Do not reply or post it back to the original group.  A suggested subject is one of the following:

Subject: EMP <Subject from the Spam message>
Subject: ECP <Subject from the Spam message>
Subject: UCE <Subject from the Spam message>
Subject: SEX <Subject from the Spam message>

Please include the original Subject: from the original Spam so that it can easily be spotted.  Thank you.

Take a careful look at the header, if there are "curious characters" (characters that look like garbage) in the X-Mailer: line, or any other line in the header, then delete those characters otherwise the message may end up truncated.  The offending line consists of the EIGHT characters D0 CF 11 E0 A1 B1 1A E1 (in hex).

If the post is particularly amusing (Spammer threat or a postmaster threat), put C&C in the subject.  Seymour tells us it means Coffee and cats. This originated from a post claiming that a particular outrageous article had caused spewing of coffee into the keyboard and jumping while holding a cat, resulting in scratched thighs.

An Excessive Multiple Post (EMP) may exceed the spam threshold and may be canceled.  An Excessive Cross Post (ECP) may not be canceled because it hasn't reached the threshold. A UCE is for Unsolicited Commercial Email, SEX is for off-topic sex-ad postings.

Make Money Fast message is immediately cancelable and are usually canceled already by others, so please do not report MMF posts.  See MMF section below.

Tracing a fake post is probably easier than a fake e-mail because of some posting peculiarities.  You just have to save and look at a few "normal" posts to try to spot peculiarities.  Most people are not energetic to go to the lengths of the below, but you never know.

Dan reminds us that first you should gather the same post from *several* different sites (get your friends to mail the posts to you) and look at the "Path" line.  Somewhere it should "branch".  If there is a portion that is common to all posts, then the "actual" posting computer is (most likely) in that portion of the path.  That should be the starting postmaster to contact.  Be sure to do this expeditiously because the log files that help to trace these posts may be deleted daily.

If you *really* want to see some fake posts, look in alt.test or in the alt.binaries.warez.* groups.

A fake post:

Path: ...!news.sprintlink.net!in2.uu.net!news.net99.net!news!s46.phxslip4.indirect.com!vac
From: XXX@indirect.com(Female User)
Subject: Femdom In Search of Naughty Boys
Message-ID: <DHLMvE.24H@goodnet.com>
Sender: XXX@indirect.com(Female User)
Nntp-Posting-Host: s46.phxslip4.indirect.com
Organization: Internet Direct, Inc.
X-Newsreader: Trumpet for Windows[Version 1.0 Rev B final beta #1]
Date: Mon, 6 Nov 1995 01:59:38 GMT
Approved: XXX@indirect.com
Lines: 13

This poor lady (Name deleted by suggestion) was abused by someone for a couple of days in an epic spam.  Many messages were gathered.  The message ID was different for several messages.  But several anomalies showed an inept poster.

The headers were screwed up, and when looking at a selection of messages from several sites, the central site was news.net99.net, where goodnet.com gets / injects news at.  This lead to the conclusion that either goodnet.com or news.net99.net should be contacted to see who the original spammer was. I never heard the results of this, but the spamming eventually stopped.

You can try looking at sites & see if they have that message by :
telnet s46.phxslip4.indirect.com 119
Connected to s46.phxslip4.indirect.com.
200 s46.phxslip4.indirect.com InterNetNews server INN 1.4 22-Dec-93 ready
head <DHLMvE.24H@goodnet.com>
430

Message was not found at that site, so it did not go thru that computer, or the article has already expired or been deleted off of that news reader.

If you wish to track a particular phrase, user-id (whatever) take a look at the URL for getting all the posts pertaining to "X" :

http://groups.google.com/

WWW IP Lookup URL's
=============================
http://samspade.org/ - My personal favorite.  All the tools you need on one page.
http://www.geektools.com/- Does lookups at all of the servers (Arin, RIPE, APNIC, etc.)
http://www1.dshield.org/ipinfo.php- Look up IP address / complaint address for Denial of Service attacks.
http://andrew.triumf.ca/cgi-bin/spamalyzer.pl- Check and see if the address is in one of the real time abuse databases.
 http://cities.lk.net/trlist.html- Traceroute Lists by States and Backbone Maps List
http://www.net.cmu.edu/cgi-bin/netops.cgi- Traceroute and ping
Index to Traceroute pages:
http://dir.yahoo.com/Computers_and_Internet/Communications_and_Networking/Software/Networking/Utilities/Traceroute/
http://www.traceroute.org/
SWITCH WHOIS Gateway:
http://www.switch.ch/search/whois_form.html
  Or
http://www.networksolutions.com/cgi-bin/whois/whois
http://www.ripe.net/perl/whois - European countries WhoIs
http://www.apnic.net/apnic-bin/whois.pl- Asian Pacific WhoIs
http://whois.nic.or.kr/- Korean WhoIs
http://www.arin.net/- North / South America WhoIs (Upper Right Corner)
IP to Lat - Lon (For those times when only a Tactical Nuke will do ;-)) :
http://cello.cs.uiuc.edu/cgi-bin/slamm/ip2ll/
Yet Another IP to name:
http://cello.cs.uiuc.edu/cgi-bin/slamm/ip2name
What do those domain names mean :
http://www.alldomains.com/alltlds.html
http://www.ics.uci.edu/pub/websoft/wwwstat/country-codes.txt- Country Codes for the last characters in a domain name


Converting that IP to a name
=============================
When all you have is a number the looks like "204.183.126.181", and no computer name, then you have to figure out what the name of that computer is.  Most likely if you complain to " postmaster@[204.183.126.181] " it will go directly to the spammer themselves (if it goes anywhere at all).

WhoIs or a traceroute will give you the upstream provider, complain to that organization.

Marty reminds us that there are some "special" IP's that are allocated as private networks.  These fall within the confines of 0.0.0.0 to 255.255.255.255 but should be ignored.  If the number is greater than 255 then it is faked.  The addresses are :

Class  Start Address  End Address
  A     10.0.0.0     10.255.255.255
       127.0.0.0    127.255.255.255 - Loopback addresses
  B   172.16.0.0     172.31.255.255
  C  192.168.0.0    192.168.255.255
  D    224.0.0.0    239.255.255.255 - Multicast
  E    240.0.0.0    255.255.255.255 - Multicast

For a full list of bogus IP addresses see:
http://www.cymru.com/Documents/bogon-dd.html
http://www.cymru.com/Documents/bogon-list.html

And a couple of other "mysterious" private IP addresses (that are not mentioned in any of *my* network books):
169.254.0.0 - 169.254.255.255 - IPV4 Auto Configuration address range (Draft RFC)
192.0.2.0 - 192.0.2.255

See :
http://www.ja.net/CERT/JANET-CERT/prevention/cisco/private_addresses.html

First off try using NSLookup (there is software for PC's, I use http://samspade.org/ , put the address in the section "address digger", click on WhoIs IP block and Traceroute and click on "do stuff" or look at the URL's at the bottom of this FAQ).  If the NSLookup does not give you a name then try a Traceroute.  Somewhere you will get a "name" and at that point I would complain to the postmaster@<that name>.  See below for complaint addresses.


What to do with "strange" looking Web links
===========================================

http://1%30%38%35%338%31%32%39%32/ has some %-encoded characters, but decoding those gives http://1085381292/
1085381292 is just another way of writing the IP address 64.177.154.172

To convert a decimal number to a "dotted quad octet" :
http://3438189385/yt/rotten1/

You can put this "strange" number in at any of the following :
http://samspade.org/ 
http://www.webspawner.com/users/ipconverter

URL Decode:
http://www.swishweb.com/dec.htm

An example of a complex URL decode:
http://home.digital.net/~gandalf/URLDecode.txt

If you look at the source HTML and you see the following then the spam has been encoded using Base64:
Content-Transfer-Encoding: base64

To decode, just copy / paste everything below the above line and click "Decode" into:
http://david.carter-tod.com/base64/

You will now have the HTML code.

This decode decodes scripts encoded with the Microsoft Script Encoder:
http://www.greymagic.com/security/tools/decoder/

http://www.netdemon.net/decode.html - This CGI handles ALL the recent types of spammer tricks, including decimal, octal, hex addresses, username/password tricks, hex encoded characters, and redirectors.

http://www.netdemon.net/tools.html - All the tools.

And you get an answer like:
204.238.155.73

You can try the "strange" number at :
http://www.abuse.net/cgi-bin/unpackit

Kirk tells us wsftp and the traceroute that comes with wsftp will take those number and automatically translate them into the IP addresses.

Or under Widows 95 :
 start --> Programs --> Accessories --> Calculator
Choose view --> Scientific
Put in the "strange" number (3438189385) and click on HEX.  You get:
CCEE9B49

Then type in each of the two characters in HEX and click DEC after each number:
CC = 204
EE = 238
9B = 155
49 =  73

Viola ... Your IP is 204.238.155.73


For more general funny URLs, like http://23123443~32:3758493879/www.samspade.org/10.00.0.1/xxxstuff.html, try http://samspade.org/

Or if that doesn't work, Andreas suggests:
Something like following does NOT work the obfuscated URL form at samspade but I figured out that these can be typed into a html-file with a texteditor or in Netscape composer 6.x in the source-mode, than loading or switching to the html mode will immediately show the decoded characters, should be an URL with a form mailer or something like "mailto:user@domain.nic"
97;&#105;&#108;&#116;&#111;&#58;&#106;&#105;&#109;&#109;&#121;&#49;&#52;&#52;&#48;&#50;&#48;&#48;&#

If you get a strange URL like:
http://www.nt.dahouc.mx^T^B^T^E^T.com|net.fr^B^E^T^B^T^E^T^T.oooooooooooooooooo.com:80/nt/dahouchy/
Where the ^B = Control "B", ^T = Control "T", etc. you can look at the very end right before the first "/" to figure out what the site is, on this case it is oooooooooooooooooo.com, using port 80.  The rest of it is "decoded" by oooooooooooooooooo.com to give the "real" site name.
For MS Windows the program at http://www.netdemon.net/ will decode these with ease.

If you are looking thru the HTML source and you get something like:
<!-- CHANGE EMAIL ADDRESS IN ACTION OF FORM --><FORM name="form" method="post" action="&#109;&#97;&#105;&#108;&#116;&#111 ;&#58;&#109;&#111;&#114;&#116;&#109;&#97 ;&#105;&#108;&#54;&#64;&#121;&#97;&#104 ;&#111;&#111;&#46;&#99;&#111;&#109;&#63 ;&#115;&#117;&#98;&#106;&#101;&#99;&#116 ;&#61;&#68;&#101;&#98;&#116;&#49;" enctype="text/plain"
Then take the "funny" looking part and paste it into the "Obfuscated URLs" section of  http://samspade.org/ like so:
http://&#109;&#97;&#105;&#108;&#116;&#111 ;&#58;&#109;&#111;&#114;&#116;&#109;&#97 ;&#105;&#108;&#54;&#64;&#121;&#97;&#104 ;&#111;&#111;&#46;&#99;&#111;&#109;&#63 ;&#115;&#117;&#98;&#106;&#101;&#99;&#116 ;&#61;&#68;&#101;&#98;&#116;&#49
And you get:
http://mailto:mortmail6@yahoo.com?subject=Debt1

So then you send a complaint to yahoo.com asking them to delete their user mortmail6@yahoo.com.

If the site is a IP address like "198.41.0.5", you can do a DNS lookup to backtrack the site.  A DNS lookup or a host command (see example below) uses the info in a Domain Name Server database.  This is the same info that is used for packet routing.  The UNIX command is :

nslookup 198.41.0.5
Commands:
nslookup hostname dns_server
  or
dig @dns_server hostname

And you get :
Name:    whois.arin.net
Addresses:  198.41.0.5, 198.41.0.6

If you are having problems with this, Josh suggests you try :

$ nslookup
Default Server:  digital.net
Address:  198.69.104.2

> set type=ptr
> 181.126.183.204.in-addr.arpa
Server:  digital.net
Address:  198.69.104.2

Non-authoritative answer:
181.126.183.204.in-addr.arpa    name = kjl.com

Authoritative answers can be found from:
126.183.204.IN-ADDR.ARPA        nameserver = escape.com
126.183.204.IN-ADDR.ARPA        nameserver = ns.uu.net
escape.com      Internet address = 198.6.71.10
ns.uu.net       Internet address = 137.39.1.3

Looking up IP address ownership

InterNIC is your friend. The InterNIC Registration Services Host contains ONLY Internet Information (Networks, ASN's, Domains, and POC's).  Please use the WhoIs server at nic.ddn.mil for MILNET Information.  Try :

Bruce tells us that there are three places where you can lookup an IP address, being the current trinity of Regional Internet Registries.  These RIRs are:

Jeef says Geektools will work out which one, as well as display the results.

Asia and Pacific Rim: APNIC - Asia Pacific Network Information Centre
        whois.apnic.net
        http://www.apnic.net/apnic-bin/whois.pl

 Americas and parts of Africa: ARIN - American Registry for Internet Numbers
        whois.arin.net
        http://www.arin.net/cgi-bin/whois.pl

 Europe and Surrounding Areas: RIPE NCC - Rseaux IP Europens, Network Coordination Centre
        whois.ripe.net
        http://www.ripe.net/db/whois.html

Under Unix, you can use:
    whois -h whois.arin.net 198.41.0.5
          or
    whois -h whois.apnic.net 198.41.0.5
          or
    whois -h whois.ripe.net 198.41.0.5

Each of the above three RIRs may refer to one of the other RIRs.  Please do not send complaints to any of the RIRs as they merely provide contact information, and are not related in any way to the possible spammers.

Dan has said that the NIC technical contact is the address to contact if there is a technical problem with the name service records for that domain.  Sending spam notifications to the zone tech contact is an abuse of the NIC WhoIs records.  Sending to the admin contact is marginally more justifiable, but should only be used after postmaster and abuse address has been tried.  Sending a complaint to all of the intermediate sites in a traceroute should *not* be done, these sites in all likelihood cannot do anything about the problem (with the exception of possibly the next to last site).

For domains that have invalid contact information you should contact the appropriate RIR (see above)

To see who the upstream provider is, try :

traceroute ip30.abq-dialin.hollyberry.com

You might get :
traceroute to IP30.ABQ-DIALIN.HOLLYBERRY.COM (165.247.201.30), 30 hops max, 38 byte packets
 1  cpe2.Washington.mci.net (192.41.177.181)  190 ms  210 ms  120 ms
 2  borderx1-hssi2-0.Washington.mci.net (204.70.74.101)  100 ms  100 ms  60 ms
 3  core-fddi-0.Washington.mci.net (204.70.2.1)  180 ms  130 ms  70 ms
 4  core1-hssi-4.LosAngeles.mci.net (204.70.1.177)  150 ms  140 ms  150 ms
 5  core-hssi-4.Bloomington.mci.net (204.70.1.142)  180 ms  200 ms  180 ms
 6  border1-fddi-0.Bloomington.mci.net (204.70.2.130)  170 ms  290 ms  240 ms
 7  internet-direct.Bloomington.mci.net (204.70.48.30)  300 ms  210 ms  270 ms
 8  165.247.70.1 (165.247.70.1)  180 ms  240 ms  180 ms
 9  abq-phx-gw1.indirect.com (165.247.202.253)  290 ms  220 ms  230 ms
10  * * *

The first column is the "hop" that traceroute is working on.  The next is the "computer" (and IP) of the computer at that hop.  The last three numbers are the milliseconds it took to get an answer from that computer.

You can get "codes" instead of the milliseconds.  An example of a "code" is the "* * *" for hop 10.

Here is a list of the codes:
? Unknown packet type.
H Host unreachable.
N Network unreachable.
P Protocol unreachable.
Q Source quench.
U Port unreachable.
* The Traceroute Packet timed out (did not return to you).

Chris clarifies that a '*' in actuality could be caused by a timeout OR something listening on the UDP ports traceroute uses to get it's port unreachables back from, to work, OR the router simply does not support ICMP/UDP unreachable ports and traceroute cannot determine it's status so it displays asterisks.

Humm..... Seems that after abq-phx-gw1.indirect.com we get no response, so *that* is who I would complain to... or you can just send a message to postmaster@indirect.com ... If that doesn't work then complain to MCI.net.

JamBreaker sez : Be sure to let the traceroute go until the traceroute stops after 30 hops or so.  A reply of "* * *" doesn't mean that you've got the right destination; it just means that either the gateways don't send ICMP "time exceeded" messages or that they send them with a TTL (time-to-live) too small to reach you.

Try  DIG (Domain Information Groper) (or one of its derivatives), it is used to search DNS records :
http://www.spacereg.com/a.rpl?m=dig
http://www.gulftech.org/webtools/webutil.pl?dig
http://tools.bintec.com/

What DIG tells you:
http://home.att.net/~marjie1/Dig.htm

yourhost> dig -x 38.11.185.89

; <<>> dig 2.0 <<>> -x
;; ->>HEADER<<- opcode: QUERY , status: NOERROR, id: 6
;; flags: qr aa rd ra ; Ques: 1, Ans: 1, Auth: 3, Addit: 3
;; QUESTIONS:
;;      89.185.11.38.in-addr.arpa, type = ANY, class = IN

;; ANSWERS:
89.185.11.38.in-addr.arpa.      86400   PTR    ip89.albuquerque.nm.interramp.com.

;; AUTHORITY RECORDS:
11.38.in-addr.arpa.     86400   NS      ns.psi.net.
11.38.in-addr.arpa.     86400   NS      ns2.psi.net.
11.38.in-addr.arpa.     86400   NS      ns5.psi.net.

;; ADDITIONAL RECORDS:
ns.psi.net.     86400   A       192.33.4.10
ns2.psi.net.    86400   A       38.8.50.2
ns5.psi.net.    86400   A       38.8.5.2

;; Sent 1 pkts, answer found in time: 64 msec
;; FROM: (yourhostname) to SERVER: default -- (yourDNSip)
;; WHEN: Thu Nov 16 23:30:42 1995
;; MSG SIZE  sent: 43  rcvd: 216

Getting a World Wide Web page busted
====================================

Many spammers use throw away accounts, accounts that they know will be deleted as soon as the service gets a complaint.  Of course the spammers mentality is "if it is free it is for me to abuse".  If the spammer really annoyed you then you might wish to dig and get every account possible deleted.  What you need to do is actually go to the WWW page that they advertise, look at the page and usually the page will redirect you to another site (or possibly redirect 2 or 3 times).  Send a complaint to these sites (with the original spam).  It is important to explain to the site you are complaining to how you got to their site so that they don't ignore you.

In Netscape and Explorer there is an option to "view source".  This will pop up a page with all of the http source from the page.  This page will have all of the "links" to the next site.

If you look at the http source and it is unreadable (and sez "Haywyre"), take a look at :
http://www.netdemon.net/haywyre/

There are spammers out there that actually have a clue.  They use open Web Proxies to reroute their web page to another location.  When you do a ping of a web site, the ping is of the open web proxy.  The open web proxy then redirects you when it gets the request for the web page.  A complete technical explanation can be found at:
http://www.google.com/groups?selm=3ee16105$1_2@nntp2.nac.net

Another thing spammers do is to abuse free WWW services to set up a web page that is encoded with Java script so that you cannot see what the html looks like.  The spammer then redirects the information to their "real" site.

http://www.spamsites.org/decode.html tells us that to decode the Java script and complain to the people that are actually hosting the spammers, set up a bookmark called "Decode Javascript" and set the URL (thanks to Code by Kicken) as the below, the code is all on one very long line:
javascript:h=document.getElementsByTagName('html')[0].innerHTML;function disp(h){h=h.replace(/</g, '\n&lt;');h=h.replace(/>/g,'&gt;');document.getElementsByTagName('body')[0].innerHTML='<pre>&lt;html&gt;'+h.replace(/(\n|\r)+/g,'\n')+'&lt;/html&gt;</pre>';}void(disp(h));

Your computer may take a while to decode all the Java, just be patient.

Usenet complaint addresses
============================================
O.K... So you have a common site that you can complain to.  Good.  If you cannot figure out where the message came from, you can post the FULL HEADERS (this is *very* important for tracing) to alt.spam, news.admin.net-abuse.misc, news.admin.net-abuse.email or news.admin.net-abuse.usenet (see the section entitled Reporting Spam and tracing a posted message).  Usually you can get someone to help with the message.

If you complain (or asked to be removed) to the spammer directly, you may just be confirming a "real" live e-mail address, which may lead to even more junk e-mail.  I would suggest complaining to the owner of the site only.  You can send e-mail to foo.bar.com@abuse.net  (where foo.bar.com is the provider you are complaining to) and it will get forwarded to the "best" e-mail address.. See http://www.abuse.net/

I used to post a long list of abuse addresses in the alt.spam FAQ, but the abuse.net lookup is much better, in fact it is the way that I look up abuse addresses.  Look up the abuse address of the ISP that you think the spammer is a customer:
http://abuse.net/lookup.phtml

There is a list of admins to contact:
http://personalpages.tds.net/~slambo/spamreports.htm

Greg reminds us that if you are complaining to a postmaster about a week-old post, don't bother.  It's not on their server, they can't verify it.  Make sure you use terms correctly.  A recent trend is to call any off-topic post "spam".  It's not.  I deal with spammers and off-topic or advertising posters differently.  Other providers do also.  Also, try to keep the clutter in your complaints down.  I don't need a copy of the referenced RFC or statute.  It doesn't help either of us if I can't find your complaint in between all the mumbo jumbo.

From : David Jackson (djackson@aol.net) (and this applies to *any* abuse) :
To report an instance of USENET abuse send mail to tosusenet@aol.com - please remember to include a complete copy of the USENET article, including all headers, to help us quickly quash the abuse.

Scott reminds us :
It might also be a good idea to remind people that sometimes the postmaster _is_ the spammer. Joe Spam might have his own domain (since they _used_ to be free) inside of which they are the postmaster. This is terrifyingly common with net.twits (kooks, etc.) but seems rare for spam. A quick note that if the spammer is the admin contact in WhoIs, notifying the postmaster will surely generate laughs on their end.

In the letter to the postmaster, you might wish to mention Joel's very good FAQ about advertising on the Internet :
http://www.cs.ruu.nl/wais/html/na-dir/usenet/advertising/how-to/part1.html
http://www.cis.ohio-state.edu/hypertext/faq/usenet/usenet/advertising/how-to/part1/faq.html

One company that was suckered in by a bulk e-mail company received 35 responses to the addresses in the body of the message, and 100% of them were negative. Additionally the ISP that hosted them received 15 complaints asking for them to terminate their service. UUNet received 50+ complaints about this UCE.

And where they *should* advertise :
http://www.cs.ruu.nl/wais/html/na-dir/finding-groups/general.html

http://www-personal.umich.edu/~jmm/papers.html#efi - Economic FAQ about the Internet

If you don't get a proper response from the postmaster, remember, WhoIs - rs.internic.net is your friend. See the section labeled "Converting that IP to a name" for more information on InterNIC.

This *should* get you a person to talk to & their personal e-mail address. If you don't get any response from that postmaster, then you should try the provider to that site. This gets a little trickier, but a traceroute should show you the upstream provider, and from there you can try contacting the postmasters of *that* site.

To contact the upstream providers first go to Merit Network Advanced WhoIs query and get their AS:
http://www.radb.net/cgi-bin/radb/advanced-query.cgi
It should look something like:
origin: AS15084

Then go to the CIDR report and get their upstreams (change the "AS15084" to something appropriate):
http://www.cidr-report.org/cgi-bin/as-report?as=AS15084
Or go to the following, scroll to the bottom and type in the AS:
http://www.cidr-report.org/

Any non-profit organization (like a University) should be very happy to help get rid of a spammer.  If the non-profit organizations resources are being used to spam a for-profit business the IRS can take their non-profit status away. Talk to the legal council at the non-profit organization if you don't get a positive response from the postmaster.

Worst case, a site can be UDP (Usenet Death Penalty) out so that other sites stop accepting news or even e-mail from that site. They are cut off from the net. Decisions like this are discussed in the news group news.admin.net-abuse.misc .

If the spammer site has problems trying to figure out where the spam came from, they can *always* get help from the denizens of news.admin.net-abuse.misc, but have them take a look at their logs first and see if they see something like (Thanks to help from Michael):

My news logs (for INND) are:
$ cd /usr/log/news
$ ls
OLD                expire.log         news.err           unwanted.log
errlog             news               news.notice
expire.list        news.crit          nntpsend.log

and here is my syslog.conf:
## news stuff
news.crit               /usr/log/news/news.crit
news.err                /usr/log/news/news.err
news.notice             /usr/log/news/news.notice
news.info               /usr/log/news/news
news.debug              /usr/log/news/news.debug

but, what they need to remember, is they HAVE TO LOOK QUICK!.  INND expire puts all these logs in OLD, and recycles them, and expires them at the 7th day (and gzips them), i.e., OLD/:
ls -l news.?.*
-r--r-----  1 news      news         181098 May 23 06:26 news.1.gz
...
-r--r-----  1 news      news         319343 May 17 06:29 news.7.gz

so... to grep an old log looking for sfa.ufl.edu:
(the {nn} is how many days ago, 1 is yesterday, 2 is 2 days ago, etc)
cd {log/OLD}
gunzip -c news.1.gz | grep sfa.ufl.edu | more


Viruses / Trojans / Spyware
===========================
If you do not have anti-virus software loaded on your computer *or* you do not have the latest and greatest virus definitions then run - do not walk - to the closest software store and buy the latest anti-virus software or download the latest definitions if you have the software and haven't updated the definitions lately.

There are several free antivirus programs available:
http://www.google.com/search?q=Free+Anti-virus
Like:
http://free.grisoft.com/doc/1 - AVG

The grief you will have if you are infected with a virus is many times the grief of loading and maintaining anti-virus software.

More and more viruses propagate thru e-mail.  If your friends machine is infected you can receive a virus from them because the virus sends a copy of itself to you (the virus send itself to everybody in your friends address book).  DO NOT open attachments even if they are from someone you know unless you are ABSOLUTELY CERTAIN the attachment is virus free.

http://www.incidents.org/react/avinfo.php - Online scanning of your hard drive and reporting viruses

If you think that you have received a virus in an e-mail, there are some online scanning tools that will scan for the latest and greatest viruses:
http://housecall.trendmicro.com/
http://www.commandondemand.com/
http://security1.norton.com/us/intro.asp?venid=sym&langid=us

You can submit the virus to your choice in anti-virus vendors, please take a look at their site to see if they have any particular submission instructions:
"Command AntiVirus" virus@commandcom.com
http://www.commandcom.com/virus/think_you_have_a_virus.html
"Computer Associates" virus@cai.com
http://www3.ca.com/virusinfo/
"F-Secure" samples@F-Secure.com
"Kaspersky AntiVirus" newvirus@kaspersky.com
http://www.avp.ru/
"Network Associates" virus_research@nai.com
http://www.mcafeeb2b.com/naicommon/avert/avert-research-center/submit-sample.asp
"SARC" avsubmit@symantec.com
http://www.sarc.com/avcenter/submit.html
"Trend Micro" virus_doctor@trendmicro.com
http://www.antivirus.com/vinfo/trendlabs/submit.htm

A Trojan is a program that you are tricked into executing that has a devious purpose. You run a small game that (in reality) loads itself onto your computer to allow someone else to get into your computer. Most anti-virus programs *should* protect against this. See:
PestPatrol Glossary
http://www.safersite.com/PestInfo/G/Glossary.asp
PestPatrol White Paper: About RATs (Remote Admin Trojans)
http://www.safersite.com/Support/About/About_Rats.asp
http://www.pestpatrol.com/whitepapers/Comparison/Product_Details.asp
Also see "A Comparison of Pest Detecting Tools" at:
http://www.pestpatrol.com/Whitepapers/Comparison/Index.asp

Spyware is software that tracks what you do at your computer and reports that information via the Internet back to the company that wrote the software.  Depending on how paranoid you are and how much you want companies to know what you are doing, you might wish to remove this software from your computer:
http://grc.com/optout.htm

Adware is software that loads itself on your computer usually without your specific permission and pops up advertisements while you are on your computer.  Both spyware and adware are usually not well programmed and should be removed.  This will make your computer run smoothly.

Scanning for Spyware:
http://www.reuters.com/newsArticle.jhtml?type=technologyNews&storyID=4306576

Spyware removal tools:
http://www.securitypipeline.com/showArticle.jhtml?articleId=57702061

To remove spyware / adware, see the below free tools.  Try one at a time and see if it stops your problem:
1) Back up any important data (this *especially* applies before taking your computer into someone to "fix")
2) Run adaware:
http://www.lavasoftusa.com/software/adaware/
3) Run Spybot Search And Destroy:
http://www.safer-networking.org/en/index.html
4) Run Hijack This
http://www.spywareinfo.com/~merijn/downloads.html
5) Microsoft Spyware Removal (I haven't used this yet, so I don't know how well it works):
http://www.microsoft.com/athome/security/spyware/software/default.mspx

There are companies spamming (and ostensibly making money) off of Trojan programs.  They tell customers they can spy on children, spouses, employees, etc (which is, by the way, illegal in the USA and many countries):
"Spy on Anyone by sending them an Email-Greeting Card!
Spy Software records their emails, Hotmail, Yahoo, Outlook, ACTUAL Computer Passwords, Chats, Keystrokes, PLUS MORE."


Fraud on the Internet and The MMF (Make Money Fast) Posts
================================================================
There are many hoaxes and frauds on the Internet. No different than RL (Real Life).

You must be very careful of any e-mail that you receive.  If the e-mail is asking for any account and password there is a very good chance that this is a fraud.  The current vernacular for this on the Internet is "Phishing".  The fraud artist is trying to get you to divulge information to them that they should not know.  Never click on a link that says anything about updating your account.  There are ways that the links you click on "look" like they are pointing to a legitimate site but in reality are pointing to the fraud site that looks JUST LIKE the real site.  If you are worried that your account may need updating, go to your browser and type in the site name by hand and then look at your account.  See :
http://www.computerworld.com/newsletter/0,4902,88583,00.html?nlid=SEC2
Also see:
http://www.computerworld.com/printthis/2004/0,4814,89096,00.html
And Suing spammers for fraud:
http://www.nwfusion.com/newsletters/sec/2004/0105sec2.html
The Washington Post wrote three articles on victims of Phishing crimes:
http://www.washingtonpost.com/ac2/wp-dyn/A59347-2004Nov18?language=printer
http://www.washingtonpost.com/ac2/wp-dyn/A59349-2004Nov18?language=printer
http://www.washingtonpost.com/ac2/wp-dyn/A61916-2004Nov19?language=printer
Australian Financial Advisor give 419ers 1 Million:
http://www.theregister.co.uk/2004/10/19/aussie_419_victim

Anti-Phishing Working Group ( http://www.anti-phishing.org ) is a coalition of financial institutions, ISPs and online retailers.  Visit their website for the latest Phishing scams that are trying to steal accounts, etc.

Many of the different organizations are creating pages to report fraud.  For example CitiBank has a page:
http://www.citi.com/domain/spoof/report_abuse.htm
And USbank:
http://www.usbank.com/cgi_w/cfm/promo/personal/fraud_email_info_and_help.cfm
http://www.usbank.com/cgi_w/cfm/personal/achieve_goals/id_theft.cfm

Donna tells us If you would like to see a safe sample of this mischief visit:
http://www.zapthedingbat.com/security/ex01/vun1.htm

Examples of the e-mails that I have received that are fraud or viruses purport that they are from E-Bay, PayPal, Amazon, Earthlink, a multitude of banks and from Microsoft.  An example of the URL (that looked like it was from Earthlink) and how it was decoded can be found at:
http://home.digital.net/~gandalf/URLDecode.txt

In addition some of these fraud artists are targeting technically unsophisticated office workers claiming they have control over the workers computer (when they really don't), or that they will get them in trouble by putting pornography on their computer unless they pay them :
http://www.computerworld.com/newsletter/0,4902,88623,00.html?nlid=PM

A partnership of the National Association of Attorneys General, the Federal Trade Commission and The National Consumers League :
http://www.fraud.org/
Call 1-800-876-7060 or fill out an on-line scam sheet:
http://www.fraud.org/info/repoform.htm
http://www.ifccfbi.gov/ - Internet Fraud Complaint Center
http://www.ifccfbi.gov/strategy/howtofile.asp - How to file a complaint - "It is important that you keep any evidence you may have related to your complaint"
http://www.ifccfbi.gov/cf1.asp - File a complaint
http://www.junkemail.org/scamspam/ - FTC ScamSpam - uce@ftc.gov
http://www.gcn.com/21_9/top-stories/18494-1.html - An article on what the FTC is doing to stop scams
http://www.ftc.gov/bcp/conline/edcams/dotcon/index.html FTC Scam Page
http://www.infoworld.com/article/03/05/15/HNftcspammer_1.html - The FTC goes against spammers
http://www.computerworld.com/securitytopics/security/cybercrime/story/0,10801,78551,00.html?SKC=cybercrime-78551 - Internet fraud is expanding.  Spam has been sent out with fake sites that "look" like real sites to steal credit card information, etc.
http://www.acidics.com/ - How all the MMF, envelope stuffing, paid to surf, read e-mail, etc scams work.  That is work for the con artists.  You (of course) lose money.

The Better Business Bureau has a web site at:
http://www.bbb.org

Hoaxes and scams :
http://directory.google.com/Top/Society/Issues/Fraud/
http://HoaxBusters.ciac.org/
http://www.scambusters.com/
http://www.wired.com/news/politics/0,1283,39298,00.html - A scam if you download a program you may pay $250 in telephone charges.
http://www.nwfusion.com/newsletters/sec/2001/00680235.html - Article on Chain e-mail, pyramid schemes, fraud

National Criminal Justice Reference Service has a site on White Collar Crimes and what to do if you are a victim.  Under More Issues:
http://virlib.ncjrs.org/MoreIssues.asp
Click on White Collar Crime:
http://virlib.ncjrs.org/more.asp?category=51=152

Virus updates, scams and hoaxes:
From Security Wire Digest ( http://www.infosecuritymag.com/digest_intro.shtml )
MTX-TESTING E-MAIL SCAMS USERS
A scam artist has been making money off gullible users by sending a virus alert about testing for the MTX Worm. The e-mail advises users to call a 900 number, which costs $2.69 per minute, for a recorded message that instructs users to visit three antivirus Web sites--sites that provide AV definitions free of charge. Always check virus alerts and possible hoaxes against hoax web sites or legitimate antivirus authorities, such as Sophos, Trend Micro and TruSecure.
http://www.vmyths.com
http://www.sophos.com
http://www.trendmicro.com
http://www.trusecure.com

In the United States :
The U.S. Securities and Exchange Commission web page (stock solicitations, stock manipulation by sending out spam after buying a stock to get others to buy the stock and increase the price) http://www.sec.gov/enforce/comctr.htm or Email:
enforcement@sec.gov
http://www.sec.gov/answers/pumpdump.htm - Pump and Dump tips
http://www.sec.gov/news/headlines/netfraud.htm - SEC prosecutions
Net Securities scam: Report to cyberfraud@nasaa.org

The Food and Drug Administration :
http://www.fda.gov/opacom/backgrounders/problem.html
Medical Items:
US Food and Drug Administration - MedWatch - Medwatch@OC.FDA.GOV
I sent Medwatch a spam about a "miracle fat removing creme" and I received the following, so for non-prescribed drugs I guess you report to the following:
Thank you for your comments. The office of MedWatch does not look into this type of complaint. This information may be given directly to FDA via the web. Please go to http://www.fda.gov.
Buying Medical Products Online - http://www.fda.gov/fdac/features/2000/100_online.html
Notifying FDA about problem Web Sites - http://www.fda.gov/oc/buyonline/default.htm

Make Money Fast is a pyramid (or Ponzi) scheme where you are in a chain of people wherein you send money to a few people and try to recruit others to send money to you. Basically if it even remotely smells like a MMF scheme it is illegal (even tho' many of the MMF schemes "claim" to have been looked at by a lawyer or checked by the United States Postal Authorities).

For a list of countries where Make Money Fast is illegal see :
http://www.stopspam.org/usenet/mmf/mmf_table.html
http://www.stopspam.org/usenet/mmf/

Please, only report MMFs in news.admin.net-abuse.misc if they're spam and you've seen it in lots of groups and / or the postmaster/user are defiantly stupid.

MMFs should be reported to the user and their postmaster and the following :
Where to send complaints to in Australia:
Ministry of Fair Trading
P O Box 6355
EAST PERTH 6536

The applicable Canadian description can be found at :
http://www.rcmp-grc.gc.ca/scams/scams_e.htm
Specifically http://www.rcmp-grc.gc.ca/scams/pyramid_e.htm
And from the Canadian Department of Justice server ( http://canada.justice.gc.ca/ ):
STATUTES OF CANADA, C, Competition - PART VI OFFENSES IN RELATION TO COMPETITION - Definition of "scheme of pyramid selling" - Section 55.1
EXTRACT FROM THE CANADIAN CRIMINAL CODE
Chain-letters
206. (1) Every one is guilty of an indictable offense and liable to imprisonment for a term not exceeding two years who . . .
Pyramid Schemes
55.1 (1) For the purposes of this section, "scheme of pyramid selling" means a multi-level marketing plan whereby ...

Norway - Sylfest tells us Norwegians should report these via email to the national taskforce on economical crime, the KOKRIM by forwarding the mail with full headers to: < desken@okokrim.no >

United Kingdoms:
Consumer Affairs and Competition Policy Directorate 2
Department of Trade and Industry, 1 Victoria Street, London, SW1H 0ET
Tel: 0171 215 0344
Have a booklet called 'The Trading Schemes Guide' which is very useful
indeed and explains the UK legal details on these things,

In the United States, you should write the Federal Trade Commission Ms. Broder
( bbroder@ftc.gov ). For more info on pyramid schemes use pyramid@ftc.gov
http://www.nwfusion.com/news/2002/0212antispam.html?net - Federal Trade Commission is cracking down on illegal spam
To find your nearest postal inspector in the USA, see URL
http://www.usps.gov/ncsc/locators/find-is.html
California MMF law :
http://www.leginfo.ca.gov/cgi-bin/calawquery?codesection=pen&codebody=endless

Another type of fraud is one where the spammer sends out a HTML message with a message / URL link that says "try a new game". When you click on the URL there is nothing related to the original message. What the spammer has (at the very least) done is gotten some money for himself by you clicking on his "click to pay" URL. Worst case the spammer may have taken advantage of a security hole in your browser and done something nefarious. Bottom line, do not click on the spammers URL, look at the e-mail and complain to the upstream provider.

And just when you thought that the spammers had reached new lows you get a spam from Word-of-Mouth.Org or WordofMouthConnection.com or womc.net (as the scam gets reported I am sure they will continue to change their name).  They purport:
"An acquaintance of your's recently shared their experience with you in our online community, Word-of-Mouth.Org. It could be a friend, a family member, co-worker, business associate, or someone else you have run into at some time.
Why are we sending you this email?
When people find out others are talking about them -- whether it is good or bad -- they want to know. At Word-of-Mouth.Org, we feel responsible to alert people so they have an opportunity to find out what is being said."

When you go to the site to find out what is being said, all you can find out for "free" is that your e-mail address is in their database.  To find out exactly what is going on you have to "join" (and, of course, pay a fee).  After you pay mysteriously your report cannot be found.  See:
http://groups.google.com/groups?q=word-of-mouth+scam
(Look at the news.admin.net-abuse.email posts)
Also See:
http://www.snopes.com/computer/internet/wordofmouth.asp
And:
http://www.nwfusion.com/newsletters/sec/2003/0901sec1.html

Yet another fraud arrives via e-mail with a subject of "Pre Action Warning." addressed to "Dear Sir" (didn't even know my name).  It specifically stated:
"I am writing to you in connection with you debt that you have with our company, Due to inflation and other factors outside of my control, your debts have exceeded       $1100.94 (one thousand one hundred and ninety four cents) I regret to inform you that we are pushing for legal action against your person.
We will offer you the opportunity to pay your debt. within the next 7 business days, if you fail to comply, our partners, hold the right to litigate on behalf of our organization."

The E-Mail went on to state that I could send Banking details, Banking Authorization, etc.  Even better it stated:
"CONFIDENTIALITY NOTICE: E-mail may contain confidential information that is legally privileged. Do not read this e-mail if you are not the intended recipient. This e-mail transmission, and any documents, files or previous e-mail messages attached to it may contain confidential and proprietary information that is legally privileged.  If you are not the intended recipient, or a person responsible for delivering it to the intended recipient, you are hereby notified that any disclosure, copying, distribution or use of any of the information contained in or attached to this transmission is STRICTLY PROHIBITED."

These are all scare tactics trying to get you to give them money and not report this to someone else.  I (of course) immediately complained to uce@ftc.gov and the two providers linked to this fraud (with the entire e-mail message and headers).  You don't owe money; they just want to make you think so.  When you get any e-mail that tells you to give someone money because they say you owe it, don't do it.  Trust me, if they want the money bad enough they won't be using e-mail to collect.

Another fraud (Bad English and all ) to try and get you to send the spammer your credit card purports:
"We have just charged your credit card for money laundry service in amount of $234.65 (because you are either child pornography webmaster or deal with dirty money, which require us to laundry them and then send to your checking account).
If you feel this transaction was made by our   mistake, please press "No".
If you confirm this transaction, please press  "Yes"  and fill in the form below.
Enter your credit card number here:
Enter  your credit card expiration date: "

As always be a cynic when receiving unsolicited e-mails.  The frauds are getting more and more complex.

Nigerian Advance Fee Fraud
============================

Robert Heinlein has a saying "TANSTAAFL" (There Ain't No Such Thing As A Free Lunch).  If it looks too good, it probably is.

There is a fraud promising you millions of dollars from a "government official" (or Widow, or son of a widow, etc.) in Nigeria (or some other small country) with a "secret" bank account, but all they need to transfer the money to you is:
(a)Your Company's Name and Address
(b)Your full Name(s), Telephone, and Fax numbers (Private and Company)
(c)Your Bank Name, Address, Account number, Telex and swift code (if any).

This is the start of the Nigerian AFF (Advance Fee Fraud).  A summary is that they ask for you to "help" pay some fees that are required to get the money out of the country, then they try to get you to go to Nigeria (or a bordering country) to meet.

At this point they try to get you into the country without a visa, promising that they will get you a visa.  At that point they have you under their control since you are in Nigeria without a visa (no, they never got you a visa) and they start intimidation (physical or otherwise) trying to get money from you.
According to the Department Of State in publication 10465 (release April 1997) "15 foreign businessmen (one American) have been murdered in Nigeria AFF scams".

The Advanced Fee Frauds can also take the form of:
Disbursement of money from wills
Contract fraud (C.O.D. of goods or services)
Purchase of real estate
Conversion of hard currency
Transfer of funds from over invoiced contracts
Sale of crude oil at below market prices

To see the details of this fraud:
http://www.wired.com/news/culture/0,1284,53818,00.html - Short Version - Meet the Nigerian E-Mail Grifters
http://www.state.gov/documents/organization/2189.pdf - The longer detailed version, Department Of State Publication 10465
Send scams to 419.fcd@usss.treas.gov (Put No Monetary Loss in the header if you haven't lost any money)
Also see:
http://www.secretservice.gov/alert419.shtml
http://www.fbi.gov/majcases/fraud/fraudschemes.htm
http://www.419legal.org/
http://www.computerworld.com/softwaretopics/software/story/0,10801,69562,00.html
http://www.nigerianfraudwatch.org/
http://home.rica.net/alphae/419coal/news1998.htm
http://home.rica.net/alphae/419coal/ - How to contact the US Gov't about this scheme
http://www.scambusters.org/NigerianFee.html - How the fraud works
http://www.cbintel.com/nigeriafraud.htm
http://www.scamorama.com/ - The Nigerian Scammers - Can you scam a scammer?
http://www.computerworld.com/securitytopics/security/cybercrime/story/0,10801,80200,00.html?nas=AM-80200 - The Nigerian Fraud continues to claim victims
http://www.nwfusion.com/newsletters/sec/2003/0224sec1.html - Two more scams, one like Nigeria scam, one demanding money you don't owe
http://www.nwfusion.com/newsletters/sec/2003/1013sec1.html - M. E. Kabay talks about scams that allege you have won a lottery in Europe.  M. E. Kabay mentions "its illegal for a U.S. resident to participate in a foreign lottery".  Again, if it looks too good it probably is

Hoaxes
=====
Lat but certainly not least there are many hoaxes circulating around the internet.  For example there is a letter circulating about "dying boy wants postcards" (Craig Shergold) which is no longer true. Same as with the Blue Star LSD addicting children hoax. See Urban Folklore FAQ at :
http://www.urbanlegends.com/classic/craig.shergold/craig_nyt.html
http://www.urbanlegends.com/classic/blue.star.tattoos/blue_star_lsd_faq.html

A complete Urban Legends listings (It is big) :
http://www.urbanlegends.com/afu.faq/index.html

Snopes offers a way to see if a photo is a hoax:
http://www.snopes.com/photos/

Some other hoax pages:
http://www.pfir.org/statements/hoaxes - Why hoaxes are damaging
http://www.symantec.com/avcenter/hoax.html - Symantec Hoax Page
http://chekware.com/hoax/ - Scams and hoaxes page
http://kumite.com/myths/myths
http://hoaxbusters.ciac.org/ - Hoaxes / Chain Letters
http://www.snopes.com/inboxer/nothing/billgate.asp - All about the Bill Gates Hoax chain letter that was followed by a hoax letter from The Gap, Bath & Body Works, Old Navy, Abercrombie & Fitch and probably just about any company you can imagine.
http://www.vmyths.com - Virus Myths
http://www.hoaxkill.com - Look on the site and see if an e-mail is a hoax and if you can't find it forward your e-mails to hoaxcheck@hoaxkill.com and they will look at it for you. If it is a hoax send it to hoaxkill@hoaxkill.com and they will notify everyone in the e-mail that the message is a hoax
http://www.faqs.org/faqs/net-abuse-faq/scams/ - Hoaxes and Scams

My usual response goes something like:
(Quote part of the hoax)
Hi! My name is Janelle McCan, Founder of the Gap. I am offering
thirty five dollar gift certificates to every seven people you send
this to.

If you ever get an e-mail that tells you to forward it to other people, it is *almost certainly* a hoax. Specifically if it tells you about a "new virus" or free money. Before you send it along *please* look it up by going to http://www.google.com and typing words from the e-mail into the search line, like (in this example) and the word hoax:
Gap gift certificates e-mail hoax

Sorry. This is a hoax. See:
http://www.snopes.com/inboxer/nothing/billgate.htm

Plus, if the Gap could trace your e-mails, don't you think the Government could do the same and wouldn't that make you worry *just* a bit? Not that they aren't trying, see:
http://www.zdnet.com/anchordesk/stories/story/0,10738,2606926,00.html

But anyway, there are no free Gap certificates, no free $1,000 bills from Microsoft or any free trips to Disney. Sorry.

PLEASE read about the Gullibility Virus. This is a very funny editorial to be passed along to your friends who send you all these kinds of hoaxes :
http://www.virtualsalt.com/warning.htm
end of hoax message

There has been some discussion that such things should be canceled because they exceed the BI 20 index. They are untrue and they waste bandwidth.


  Open system spammers love
================================

FormMail is a free program used by many legitimate sites to glean data submitted via online forms. Last year, a vulnerability was discovered in the FormMail.pl gateway that allows external users to run the program. As a result, unsecured FormMail installations have become favored targets with junk emailers.

Many of the viruses circulating now leave "back doors" into the computers that they infect.  Armed with the knowledge of the back door, spammers hijack the computer and use the hijacked computer to send out their spam.

Of course open SMTP servers are ALWAYS the computer of choice to blast a few million e-mails out with.

Bottom line, the owner of the computer is responsible for keeping their computer secure.  Complain to the upstream provider about their customer and get the computer disconnected from the network until the problems can be corrected.


Filtering E-Mail BlackMail, procmail or News with Gnus
=======================================================

Filtering with BlackMail. This is free software that works with Mailers Smail, Sendmail, Qmail or Fetchmail under the OSes: Aix, various BSD, Irix, Linux, NeXTStep 3.x, Solaris, SunOs, SVR4:
http://www.jsm-net.demon.co.uk/blackmail/blackmail.html - Written by Ken Hollis (Not me ...) and maintained by James Murray
Or
http://www.jsm-net.demon.co.uk/blackmail/source
Get the procmail FAQ :
http://www.ii.com/internet/faqs/launchers/mail/filtering-faq/
or
http://www.best.com/~ii/internet/faqs/launchers/mail/filtering-faq/
http://www.ii.com/internet/robots/
or
http://www.best.com/~ii/internet/robots/
Procmail ruleset :
http://www.impsec.org/email-tools/procmail-security.html

Or read about it when it is posted to :
Newsgroups: comp.mail.misc , comp.mail.elm , comp.mail.pine , comp.answers , news.answers
Subject: Filtering Mail FAQ

Bob tells me that Eudora Pro has a good filtering capability. You can filer based on who you send e-mail to, known spammers, etc. Enough filters and you may see hardly any Spam. Claris E-Mailer, likewise, has a filter option.

Brian has a Gnus scorefile from the Internet blacklist :
http://www.cs.ubc.ca/spider/edmonds/usenet/gnus/BLACKLIST
Or his example global scorefile :
http://www.cs.ubc.ca/spider/edmonds/usenet/gnus/SCORE

Many news readers have a "kill" file that will filter out the posts from either a certain user-id, or posts with certain titles. Each news reader is unique. You might wish to read the help file on the subject of kill files.

Columnist Al Fasoldt suggests a method for filtering your own e-mail:
http://www.twcny.rr.com/technofile/texts/bit121901.html


Rejecting E-Mail from domains that continue to Spam
====================================================
Spamfilter can be found at:
http://www.samiam.org/spam/index.html

See Sendmail site: http://www.sendmail.org/
Ask your admin to add the following to their sendmail.cf.  This will reject all mail that continues to come in from domains that only send out spam.  This is a group effort from many admins :
Modify your sendmail.cf in the following way.
1. Setup a hash table with the domains you wish to block:
# Bad domains (spam kings)
FK/etc/mailspamdomains

2. Add the following rules to S98 (be sure that there are three lines (i.e. the lines are not split up) and be sure to put a TAB character between the $* and the $#error, not a space) :
### Spam blockage
R$* < @$*$=K . > $*       $#error $@ 5.1.3 $: "Your domain has been blocked due to spam problems.  Contact your administrator."
R$* < @$*$=K > $*          $#error $@ 5.1.3 $: "Your domain has been blocked due to spam problems.  Contact your administrator."

3. Make your hash table.  Here is a very small example :
moneyworld.com
globalfn.com

Mail that comes in from any of these domains will be returned to sender with the error.  If the sender is bogus, it will bother the postmaster at the bad domain in an appropriate manner.

Keep in mind that *ALL* email from these domains will be blocked.  This is really only a good solution for domains that are setup by spammers for spamming.  Blocking something like aol.com, although it may seem initially attractive, would cause problems for legitimate users of email in that domain.  Compile your list after careful verification that these domains fit the above description.


   Misc.
=================================

Protection for you and your kids on the Internet
=================================================
The kids have learned the Internet first, and there is a good point made that the Internet may be the first "system" created where kids are teaching parents about ethical use of the Internet.

Learn about it yourself to help your kids use the Internet responsibly.  When educating yourself, be *very* sure to read all privacy notices (or anti-privacy policies in this instance).  Many of the online contests have "privacy" policies that (basically) say that they can sell any and all information that you submit to anybody that they feel like.  That could include selling your e-mail address to spammers.  Even when you make an online purchase, scrutinize the privacy policy.  An example of a company who's privacy policy allows them to redistribute your information is Ticketmaster.  See:
Ticketmaster's Privacy Policy: Opting Out is Not an Option
http://www.gripe2ed.com/scoop/story/2003/7/24/84435/6284

http://www2.norwich.edu/mkabay/cyberwatch/index.htm - Protecting yourself and your kids on the Internet, teaching your kids about ethical Internet Use

http://www.ftc.gov/bcp/conline/edcams/infosecurity/ - FTC generic information about keeping secure on the Internet.  In addition there is a Childs quiz about being a safe cybersurfer.

A company "Alyon Technologies" installed a dialer on home computers and connected / charged the consumers for pornography calls even when they were away on vacation:
http://www.channel3000.com/technology/2189632/detail.html

http://www.cheycobb.com/comp_sec_advice.html - Computer security for non geeks
And her book:
http://www.amazon.com/exec/obidos/ASIN/0764516795/102-0644946-4499357


I am interested in eliminating spam from my emails, how do I do this?
=====================================================================
First off NEVER reply to the "Remove Me" e-mail addresses or sites.  This only confirms that you have a live e-mail address and makes *your* e-mail address more valuable to sell to other spammers.

Start off by reading this spam FAQ.

It may take a while to digest all of the new information, but just read it and see what you can get out of it.

Start complaining to the ISP (Internet Service Provider) of where the spam came from.  Understanding the "Received:" headers is key to this.  Trace back in the Received: header to where it looks like the spam came from and complain to that provider about the spam.

Look in the body of the e-mail.  If someone tells you to reply to back to a e-mail address or if they point you to a web site then complain to the ISP owner of that web site or e-mail address (NEVER complain to the spammer, they already know it is wrong and will ignore you).

These steps will help get the spammers accounts eliminated.

Will it stop you from getting spam?   Probably not.  If spammers have your e-mail address it is already too late.  They are selling your address to each other, passing it around.  About the only way to do that is to change your e-mail address and give it out to as few people as possible.


   Origins of Spam
======================
The history of calling inappropriate postings in great numbers "Spam" is from a Monty Python skit (yes, it is very silly... see http://www.ironworks.com/comedy/python/spam.htm ) where a couple go into a restaurant, and the wife tries to get something other than Spam. In the background are a bunch of Vikings that sing the praises of Spam. Pretty soon the only thing you can hear in the skit is the word "Spam". That same idea would happen to the Internet if large scale inappropriate postings were allowed. You couldn't pick the real postings out from the Spam.

The very first spam was on 2 May 1978 from Digital Equipment Corporation (DEC):
http://www.templetons.com/brad/spamreact.html

The different kinds of "spam":
spam – Unsolicited (Commercial Or Bulk) E-Mail
SPIM or spIM - IM Spam, Cell Phone SMS spam
SPIT - Spam over Internet Telephony

Geek cartoons, some anti-spam cartoons mixed in:
http://www.userfriendly.org/cartoons/archives/ - Type "spam" and click "Submit Query"
http://ars.userfriendly.org/cartoons/?id=19990226 - :-)

The Spammers Rules (and their lies):
http://bruce.pennypacker.org/spamrules.html
http://groups.google.com/groups?q=Rules+of+Spam

To join a discussion list for Spams, send a message to listserv@internet.com
In the body of the message type :
subscribe spamad your_name your_affiliation
Or a real mailing list for the discussion on spamming and about what is and/or isn't possible in dealing with this problem. If you would like to join the mailing list send mail to majordomo@psc.edu with the following message in the body :
subscribe spam-list [preferred address]

Oldmilk tells us the alt.spam Commandments :
1) Thou shalt not post binaries to a non binary group.
2) Thou shalt not post "sPaM this l00zer" to alt.spam
3) Thou shalt not post to inform us for the thousandth time that this group was started to discuss the fine spiced ham product from Hormel.
4) Thou shalt not spam this newsgroup.
5) Thou shalt not post on a topic that has nothing to do with spam fighting.
6) Thou shalt not harass any regular poster here, lest your ass be spanked to rosy hue.
7) Thou shalt not attempt to make any straw man arguments that spam is good.
8) Thou shalt read the newsgroup before posting.

First off, the only CORRECT way to "SPAM" the net :
http://www.spam.com/
http://www.spam.com/fc.htm - SPAM Fan Club
http://www.spam.com/ci/ci_in.htm - Spam, SPAM and the Internet ... Use "Spam" when referring to Internet Unsolicited E-Mail, ONLY use "SPAM" (all CAPS) when referring to the Hormel Product.
Show SPAM Gifts http://www.spamgift.com/
Or for the free SPAM recipe Book ($1.00 postage and handling) :
SPAM recipe Book, P.O. Box 5000, Austin, MN 55912
Or for SPAM merchandise and apparel call 1-800-LUV-SPAM
SPAM Sites (the food) / The Church of Spam :
http://www.spamhaiku.com/ - SPAM Haiku
http://www.go2net.com/internet/useless/useless/spam.html
http://www.vivalasvegastamps.com/spam.html

A conversation with a spammer. I was amused. First time I had ever spoken with one. I also forgot to mention (in our very short conversation) that his World Wide Web service would be deleted (which it was) :
Me (7:04 PM): I got your spam. By Monday morning all your accounts should be canceled. That would be your AT&T account, your Hotmail account and this AOL account. You are welcome. Bye.
GS711 (7:05 PM): snip - Expletive Deleted
Me (7:05 PM): Thank you very much. You should learn how to advertise correctly on the Internet.
Me (7:06 PM): If you do it correctly than you won't have to run and hide.
GS711 (7:06 PM): thanks for letting me know who you are
Me (7:06 PM): Who am I? :-) ...
Me (7:06 PM): BTW, all your Spams will be reported by many other people other than myself ...
(He signed off)
And another exchange with a spammer:
http://petemoss.com/spamflames/ShifmanIsAMoronSpammer.html
Just keep the spammer in a conversation - http://www.thespamletters.com/

A Spammers Soliloquy. I had to keep this one because it was actually very creative (unexpected from a spammer) :
http://digital.net/~gandalf/spammersoliloquy.html

And if you cannot get enough Unsolicited Commercial E-Mail, you can listen to it coming from your speakers:
http://spamradio.com/html/listen.html

And a final note to spammers (I try not to make too many "personal" statements in this FAQ ...). It is best not to be such a pain that the Geeks find an intense interest in you. They are almost certainly smarter than you, at the very least they are smarter in the ways that the Internet works. The worst thing for you, however, is that they usually have no life and can easily make you "their life".


How *did* I get this unsolicited e-mail anyway?
==================================================
Unfortunately just posting a message to a news group can get unsolicited e-mail. Some spammers "harvest" e-mail addresses by stripping e-mail return addresses out of messages people post. Try posting to alt.test a few times. You will get not only a few autoresponder messages (that is how it is *supposed* to work) but also a few unsolicited pieces of e-mail. The solution to this is to "mung" your address when you post by adding in extra characters (like "Spam") in your return address. You then put in your signature something like "Remove the word Spam from my e-mail to contact me". See:
http://www.private.org.il/harvest.html - How spammers harvest addresses
http://home.cnet.com/software/0-3227888-8-6602372-1.html - Riskiest e-mail behaviors on the Net
http://members.aol.com/emailfaq/mungfaq.html - Address Munging
http://gamesbyemail.com/Documentation/AntiSpamEmailLinks/ - Examples of disguising your e-mail.
http://www.applelinks.com/articles/2001/07/20010730122944.shtml - converting email addresses to "digital entities"
http://www.inter-linked.com/content/spiderbait.php3 - A Java script to encode your e-mail address on a web page
Larry suggests making your e-mail address into a JPEG (picture).  You can't click on it and send a e-mail, but the spammers can't harvest your e-mail address either.

*Do Not* ever reply to the "unsubscribe" option in a spam. That only confirms your e-mail as "real" and gets your e-mail address sold to others. More spam for you.

Another way to get e-mail is to have a World Wide Web page. Some spammers just start a web spider (a piece of software that just traverses World Wide Web pages and collects information) going and collect e-mail that way. To prevent your e-mail from being harvested, you can "mung" your web e-mail.

Yet another way for spammers to verify your address is real is to have multiple unique pages to their site so that when you click on the URL they provide, they know that you (and only you) got that URL. See:
http://cnn.com/2000/TECH/computing/01/14/email.privacy.idg/index.html

Greg tells us of yet another clever trick. The spammer imbeds a unique image (Web Bug) in a spam e-mail so that just the act of opening the e-mail tells the spammer that your address is "live":
img src="http://209.73.247.130/cgi-bin/loadbalance/load.cgi?servers=clusters_1-9 & image=39E218DC0DE2341934ED231E203E382D2193A7B975B23CA8EA-3.jpeg" border=0

I have seen yet another trick that spammers use, they make the URL a web bug.  When you have a link like http://NAIOKWDVDISY.adwarebde.com/?id=02025 the "name" of the web site NAIOKWDVDISY can uniquely identify what e-mail address that spam was sent to.  Just doing a NSLookup of the name will point out the e-mail address of the person that the spam was sent to thus identifying a "live" person.

Pierre suggests that when putting a mailto URL in a web page, precede and follow it with "%20". When someone clicks on it, it will merely put spaces, which will be ignored, around the address, but when a spammer harvests the address, it will have a %20 in it, which will render it undeliverable.

A suggestion of some nasty little HTML items to have in your WWW page (invisible, of course) are :
<A HREF="mailto:root@[127.0.0.1]"></a>
or if your server allows "server-side includes" (and .shtml) :
a<A HREF="mailto:abuse@!--#echo var="REMOTE_ADDR"-- "anti spambot></a>

Also you might include a mail to news gateway like the following so that the Spam is posted to Usenet :
See https://ssl.dizum.com/help/mail2news.html for mail to news gateways.
A HREF="mailto:news.admin.net-abuse.email@myriad.alias.net"/a
Or
A HREF="mailto:news.admin.net-abuse.misc@myriad.alias.net"/a
Or
A HREF="mailto:news.admin.net-abuse.usenet@myriad.alias.net"/a
Note : You should note on your World Wide Web page that these links should *not* be followed by Lynx users, as they will see them no matter how you choose not to display them on a graphical interface. The last few in the below list are particularly not nice as they execute commands on a UNIX host. Substitute root@[127.0.0.1] with any of the following :
postmaster abuse root admin postmaster@localhost abuse@localhost root@localhost admin@localhost postmaster@loopback abuse@loopback root@loopback admin@loopback
`cat /dev/zero /tmp/...`@localhost
;cat /dev/zero /tmp/...;@localhost
`umount /tmp`@localhost
;umount /tmp;@localhost
`halt`@localhost
;halt;@localhost


   Can I find the persons name and phone from an e-mail address
==================================================================
The short answer is no, not unless the person isn't very smart.  The only person that can definitively tell you who owns that e-mail address is the ISP (i.e. rr.com, digital.net, etc).  They will most likely not tell you this information unless you have a warrant from the police forcing them to do so.  You *might* find something if you search for any e-mail addresses that they used and see if it pops up any information:
http://www.google.com/ - Search the Internet
http://groups.google.com/ - Search Usenet


   How To Respond to Spam
===========================

Howard reminds us :
Note to all:  NEVER follow-up to a spam.  NEVER.  Express your indignation in mail to the poster and/or the postmaster@offending.site, but NEVER in the newsgroups!

Karen asks:
But what about the newbies who look at a group, see lots of spam and ads, see NO posts decrying them, and conclude that ads are therefore OK?

Ran replies :
When it gets bad,  you'll usually see some "What can we do about this?" threads.  That's a good place to attach a reply that tells people why it's bad, and what they can, in fact, do.

Austin Suggests:
At the risk of attracting flames, let me suggest an exception to Howard's law.  A follow-up is allowed if the following 3 conditions hold.
   1) The offending article is clearly a SCAM (for instance, the *Canada* calls with the Seychelles Islands phone # scam)
   2) No one else has followed-up with a posting identifying it as a scam (in other words, no 'Me too' warnings)
   3) It is unlikely to be canceled soon, either because it seems to be below the thresholds, or it is in a local hierarchy that doesn't get cancels, or Chris Lewis is on vacation in the Seychelles Islands.  If all three conditions are met, a follow-up that X's out the contact information , severely trims the contents and identifies the post as a scam is exempt from Howard's law.
Bill's and Wolfgang's addition :
   4) Follow-ups should be cross posted to news.admin.net-abuse.misc _and_ the groups of the spam, but Followup-To: *MUST* be set to news.admin.net-abuse.misc *ONLY*
_or_
post a follow-up and *SET* Followup-To: alt.dev.null.
In the first case change
 Subject: Important FREE $$$
to
 Subject: Spam (was Re: Important FREE $$$)
and include the original Newsgroups and Message-ID line, so the professional despammers will immediately find what you're talking about.  Do not post unless you're absolutely sure that you can do all that properly. Also 1) - 3) do apply.

If you see the same article with different Message-IDs in several groups, collect the _complete_ headers of each article and check news.admin.net-abuse.misc if it's already been reported. If not, start a thread with Subject: Spam (was Re: <original Subject>) in news.admin.net-abuse.misc or news.admin.net-abuse.usenet . Include all of the headers and as much of the body of one article as you see fit.

Shalon adds:
One note here: in the soc.subculture.bondage-bdsm group, we have 3 or
4 netcops who *do* follow up each spam message with header, WhoIs,
traceroute, and contact address info so that those in the group who do
not have the technical skills to determine this can complain. It's an
unmoderated sex-related newsgroup which has almost no spam -- so it
would appear that the technique works extremely well.

  Firewalls and protecting your computer
========================================

If your computer is constantly connected to the Internet (DSL, cable modem, thru a corporate connection) you should have *some* kind of software or hardware that monitors to keep hackers out.

You have no excuse for not installing virus and firewall software on your computer.  There is always someone out there offering free or low priced antivirus or firewall software. See http://www.google.com/search?q=free+antivirus+firewall
For example:
http://www.my-etrust.com/microsoft/

CERT has released a white paper designed to help technical folks spread the word to home users about Internet security:
http://www.cert.org/tech_tips/home_networks.html

A description of what a firewall looks for / can tell you is at:
http://www.robertgraham.com/pubs/firewall-seen.html

Review and explanation of firewalls:
http://grc.com/su-firewalls.htm

An example of personal firewall software is:
http://ntbugtraq.ntadvice.com/ - Click on the FAQ link and there is a link to a page with a very extensive  list of firewalls.
http://www.google.com/search?q=Personal+Firewall - Google search for personal firewalls

The problem with some of these types of software is that they are "technical" when they report an "attack" and the "attack" may or may not be worth noting. ZoneAlarm by Zonelabs and Network Ice (Black Ice) seems to work fairly well IMHO, but again you will need to examine each "attack" and see what it really is before complaining to a provider.

Bottom line, if you are constantly connected to the Internet (or even if you dial up for long periods of time) you should either have a firewall in your network, or run software like the above.


  Revenge - What to do & not to do
========================================
No matter how much we hate Spam and how much we dislike what the spammers to our quiet little corner of the Universe known as the Internet, Spam is not illegal worldwide (yet). If you try anything against the spammers, please * do not * put yourself in risk of breaking the law. It only makes them happy if you get in trouble because you were trying to get back at them.

The reason why spammers use "throwaway" accounts is because they know the e-mail account will be deleted. They usually provide either another e-mail address or a name / phone number or postal address so that prospective "customers" can be contacted. Be sure to complain to the postmaster of all e-mail names provided to make sure that this route is inhibited.

There are sites dedicated to revenge, just search in Google.  Jeff mentions that some people cross enter 800 numbers, phone numbers and addresses of spammers onto other spammers' sites.  He says the least we can do is introduce like minded individuals to each other. Just being neighborly. ;-)

You can ask the Attorney General of a state whether or not that business is licensed in that state, and who runs the business. I looked up a business out of Nevada and found :
http://www.naag.org/ - National Association of Attorney Generals
http://ag.state.nv.us/ - We welcome any comments or concerns from you regarding Attorney General matters. If you would like a response from this office, please provide your name, address and telephone number, with your electronic inquiry and this office will respond to you by mail.
Write to : AGINFO@ag.state.nv.us

Look the business name / owner up on the WWW for Las Vegas NV :
http://sandgate.co.clark.nv.us:8498/businessLicense/blindex.htm
Which gave me the following info for the spammer "ROAD TO WEALTH INC":
http://sandgate.co.clark.nv.us:8498/servlet/BusinessLicense?instance=blotdetl&license_number=1000144-533
And see if they are paying the correct taxes:
http://tax.state.nv.us/
Nevada Department of Taxation
555 E. Washington Ave.
Suite 1300
Las Vegas, NV 89101
PH: (702)486-2300
FAX: (702)486-2373
City of Las Vegas
Department of Business Services
P.O. Box 1900
400 Stewart Avenue
Las Vegas, NV 89125
(702)229-6281

  Telephoning someone
======================

Calling someone once is fine.  If enough people are irritated at the spammer and they all call the 1-800 number the spammer provides, the spammer will get the idea (sooner or later) that it is costing them more in irate people (and most especially loss of business) and it is not worth it to spam.

Do not dial any phone numbers more than once from your home.  Phone harassment is * illegal * and you * can * be prosecuted in court for this. Even tho' the caller id blocking code (may be *67 or *71 or some other code) prevents your number from being displayed on their telephone at home if they have caller ID, *57 will give the phone company the number, *69 will dial back the phone number via automatic call back.  If it is a 1-800 number there are two problems.  First they can *always* get your phone number, and secondly it may *not* be a toll free number.  You may be charged for calling a 1-800 number.  Of course calling from a pay phone takes care of all of these problems :-) ...

Likewise, do not call collect using 1-800-COLLECT or 1-800-CALL-ATT from home, once again this can be traced.

Austin comments : I would say that calling a listed non-800 number *once* collect to voice a complaint is not harassment, but justified.  They sent you a postage due message, didn't they?  If they don't want to accept collect calls, they should say so - and if they do, you should be a responsible person and not do it again.

AT&T Information for 1-800 numbers is 1-800-555-1212, but that only helps if you know the company name you are trying to call.  Also, you can try searching for a 1-800 number (you do not have to know the company name) at :
http://www.anywho.com/tf.html
Other telephone search mechanisms:
http://www.infospace.com/info.zip/
http://www.bigbook.com/
http://www.switchboard.com/
http://decoder.americom.com/ - Look up location by area code.
http://www.nanpa.com/area_codes/index.html - North American Numbering Area Code Lookup
http://www.aegisbooks.com/download.html - Map of the Area Codes


Snail Mailing someone
=======================

Likewise, one well thought out letter sent to the spammer might help convince the spammer not to do this again.  Especially if the spammer was part of a corporation that didn't realize the detrimental effects of spamming the Internet.

If you decide to deluge the spammers postal address by filling out one or two "bingo" (popcorn) postage paid cards in the technical magazines (by circling a few dozen "product info" requests per card & putting on printed out self sticking labels with the spammers address), or by putting preprinted labels on postage paid cards that come in the mail in the little plastic packages, don't organize a public campaign (that they can point to) against the spammer in the newsgroup.

Scott also reminds us :
Since this is the "Spam FAQ", I'd like to point this out: You're basically Spamming the company offering information in a magazine.  It costs companies money, not the one you're spamming. They get a free pile of junk which is easy to throw out. In other words, this may be harming third parties more than the intended target.  I'm not trying to be Mr. Nice Guy, just trying to point out an important technicality.

Organizing a campaign against the spammer could lead to the spammer trying to get a cease & desist police order against the organizers.  Likewise, FAXes that are inverse pages (black background on white letters) to a spammer could probably give you problems.


1-900, 1-800, 888, 877 and 1-### may be expensive long distance phone calls in the U.S.
==============================================================
http://www.ftc.gov/bcp/conline/pubs/tmarkg/nine.htm - 1-900 explained
http://www.ftc.gov/bcp/conline/pubs/services/cramming.htm - Mysterious Phone charges
http://www.theregister.co.uk/2004/09/22/ireland_rogue_dialler_crackdown - Long distance charges on your phone bill from your modem
Be very careful when dialing a 1-800 or any "toll free" number you are not familiar with. It may end up being a very expensive mistake. Remember to dial these numbers from a phone booth so that your home phone will never be charged. Another reason to call from a pay phone is so that the spammer cannot get your home phone number. Even if you are "Unlisted" when you call a toll free number the spammer gets your phone number.

All 1-800, 888 or 877 numbers are *not* free in the United States. Ozzy tells us that in Canada, ALL 1-800, 866,877, & 888 numbers ARE toll free.  In the U.S you may be charged for the phone call. You can tell if the number charges by calling from a phone booth. If you cannot get through then it charges. See below.
Likewise, numbers that may "look" like they are United States long distance phone numbers may in fact be out of country and may cost you $25 or more for a couple of minutes call. These calls are not refundable. A scam artist trying to get money from the phone calls (he gets a skim off the top) was dialing random beepers with an out of country number.

A phone scam can be read at http://www.scambusters.org/809Scam.html

Some area codes to look for (some may not be active for another year or two):
(Also see http://docs.nanpa.com/cgi-bin/npa_reports/nanpa?function=list_npa_geo_number )

242 Bahamas
246 Barbados
264 Anguilla
268 Antigua
284 British Virgin Islands
340 U.S. Virgin Islands
345 Cayman Islands
441 Bermuda
473 Grenada
649 Turks and Caicos
664 Monserrat
670 CNMI (Commonwealth of the Northern Mariana Islands?)
671 Guam
758 St. Lucia
767 Dominica
784 St. Vincent and Grenadines
787 Puerto Rico
868 Trinidad and Tobago
869 St. Kitts and Nevis
876 Jamaica

If the ad says "Procall", it is a large service bureau for 1-900 numbers in Arizona.  When you call a pay-per-call number, there should be a recorded intro that will give a customer service number.  That *should* connect with a live person.

I would like to thank Eileen at the FTC for kindly answering my questions about 1-900 & 1-800 phone numbers.

Paraphrasing what she e-mailed me :
When a 1-900 number is advertised, the price must also be disclosed (this may be found at 16 CFR Part 308).

When calling a 1-800 number that charges, there must be an existing subscription agreement between the buyer and the seller

http://www.ftc.gov/ Federal Trade Commission Home Page
http://www.ftc.gov/bcp/telemark/rule.htm Telemarketing Sales Rule
http://www.ftc.gov/bcp/conline/edcams/telemarketing/index.html - Telemarketing information / scams
http://www.ftc.gov/bcp/conline/edcams/telemarketing/fileacomplaint.htm File a complaint
http://www.infoworld.com/article/04/09/07/HNspamspit_1.html - Spam over Internet Telephony (SPIT)


Junk Mail - The Law
===================

http://www.jmls.edu/cyber/index/spam.html - Collection of legal spam items
http://www.lectlaw.com/ - 'Lectric Law Library
http://spamlaws.com

Kevyn tells us that : In many countries, forgers of headers can be prosecuted. This is the equivalent of forging a postmark and delivering it yourself. When someone sends out spam with forged headers, he or she clearly:
a) knows that what they are doing is wrong, and that they can be punished for it
b) is clearly attempting to evade detection and punishment.

For Norwegians, these pages may be interesting:
http://www.datatilsynet.no/
(Datatilsynet is a government controlled organization, made to
protect people's right to privacy. This page explains that if someone
wants to advertise by email or SMS messages, they need prior consent
from the victims)

You should also read Title 47 of the United States Code, Section 227. There is a FAQ at cornell.law.edu for the text of the law (gopher or ftp or http://www.law.cornell.edu/uscode/47/227.html ), and you can use http://groups.google.com/ to read the USC 47 thread on news.admin.net-abuse.misc to make up your own mind (it invariably comes up) or you can look at :
http://www.cybernothing.org/docs/code47.5.II.txt

In Washington (State) (for example) fax laws (RCW 80.36.540 - Telefacsimile messages) define "telefacsimile message" in such a way that could be interpreted to include E-mail. It was not originally written to cover E-Mail, but that is for the courts to decide :-). California regulates it thru Section 17538(d) of the Business and Professions Code.
http://www.newsfactor.com/perl/story/11103.html - Washington State's highest court upholds anti-spam law.

Spammers that have actually been prosecuted. See:
http://www.bibliotech.net/spammer.html

In California (Quoted from http://Spam.abuse.net ): Spamming to or from California e-mail service providers against their policy is now a civil offense under California Business and Professions Code Section 17538.45. If you run a California-based e-mail service provider, you need to notify your customers of the law and your anti-spam policy in order to be eligible to collect damages of $50 per message.

Jeff tells us the California Code referring to spam (CA Bus. Prof. Code Sections 17538.4 and 17538.45) may be found through clicking "All" and entering "17538" into:
http://www.leginfo.ca.gov/calaw.html (A pretty authoritative source) then click on "BUSINESS AND PROFESSIONS CODE"
 Also see:
http://www.netatty.com/spam.html - Sue a California spammer
The Virginia law : http://www.spamlaws.com/state/va.html
The Washington State Law : http://www.wa.gov/ago/junkemail/
Spammers successfully sued - http://www.woodyswatch.com/windows/archtemplate.asp?4-13#watchdog
The Federal Computer Fraud and Abuse Act : http://www4.law.cornell.edu/uscode/18/1030.html


Additional Resources - Lots Of Links
=============================================================

The latest & greatest version of the Spam FAQ is found at:
http://digital.net/~gandalf/spamfaq.html
(or http://home.digital.net/~gandalf/spamfaq.html )
Or *nicely* HTML'ed at:
http://www.cs.ruu.nl/wais/html/na-dir/net-abuse-faq/spam-faq.html
http://fuzzo.com/spam_faq.htm
or
http://www.faqs.org/faqs/net-abuse-faq/spam-faq/
Or the archive at:
ftp://rtfm.mit.edu/pub/usenet/alt.spam/
ftp://rtfm.mit.edu/pub/usenet-by-hierarchy/news/admin/net-abuse/misc/
http://samspade.org/d/nanaefaq.html - news.admin.net-abuse.email FAQ
http://www.abuse.net/books.html - Spam Books
http://www.spamfaq.net/terminology.shtml - spam terminology
http://www.cm.org for info on NoCeM
http://www.killfile.org/faqs/spam.html
Net abuse jargon:
http://www.ncf.carleton.ca/ip/freenet/subs/complaints/spam/jargon.txt
http://groups.google.com/groups?selm=6tk5th%24497%40freenet-news.carleton.ca
Software to track the headers / eliminate Spam for you :
http://www.antispam-software.net/ - Anti-Spam software for Outlook and AOL
http://allmacintosh.forthnet.gr/macintosh.html - Mac software
http://samspade.org/ - Sam Spade WWW Spam tools - Excellent!
http://www.spamulor.net/ - Software to identify / classify and funnel spam to a location out of your way
http://www.exit109.com/~jeremy/news/cleanfeed.html
http://www.julianhaight.com/spamcop.shtml - Spam Cop - Does the header analysis for you.
http://www.netdemon.net/ - 30+ spam tools ...
http://www.spamhippo.com/
http://www.spammerslammer.com - Works with windows e-mail programs that uses pop mail
http://www.vicomsoft.com/knowledge/reference/spam.html - Vicomsoft document to raise awareness about Spam and offer practical solutions to email users
http://www.vipul.net/ricochet/ - automated spam tracing and reporting agent
http://andrew.triumf.ca/pub/security/ - UNIX Tools
Your Daily Spam News:
http://www.spam-news.com
http://www.newsadmin.com/cgi-bin/newsspam1 - Top Spam Hosts
http://www.newsadmin.com/cgi-bin/newsspam2 - Top Spam Sites
Spammers and how to stop them :
http://abuse.sourceforge.net/ http://spam.sourceforge.net/ - Anti-spam support site
http://livinginternet.com/e/et_spam.htm - a discussion on the origins of spam
http://spamhaus.org - spam havens listing
http://lumbercartel.freeyellow.com/  - http://www.cafeshops.com/tinlc - TINLC - There Is No Lumber Cartel - CafeShops has the TINLC Tee-Shirt.
http://dir.yahoo.com/Computers_and_Internet/Communications_and_Networking/Email/
http://dir.yahoo.com/Computers_and_Internet/Communications_and_Networking/Email/Spam/
http://www.computerworld.com/softwaretopics/software/groupware/story/0,10801,75737,00.html - Spam wars
http://home.att.net/~marjie1/ - Spam killer central
http://home.att.net/~marjie1/faq.htm - FAQ and gives how to view headers (about half way down)
http://home.att.net/~marjie1/Glossary.htm - Glossary of terms
http://www.jahitchcock.com/cyberstalked/ - Maryland Anti-Harassment bill
http://morehouse.org/hin - Internet Security
http://persona.www.media.mit.edu/judith/Identity/IdentityDeception.html
http://www.cromwell-intl.com/security/ - Internet Security
http://slashdot.org/articles/99/08/02/129213.shtml - ISP sues spammer
http://spam.abuse.net/spam/
http://viper.law.miami.edu/~froomkin/articles/oceanf.htm Regulation of Computing and Information Technology
http://www.atnewyork.com/news/article.php/1557541 - AOL wins against Spammers
http://www.abuse.net/lookup.phtml - Complaint lookup
http://www.antionline.com/ - Internet Security
http://www.cabal.net/jason/index.html - A spammer tries to sue the Cabal (TINC)
http://www.cabal.net/ - The Cabal (TINC)
http://www.cauce.org - Trying to legislate against http://www.ecofuture.org/ecofuture/jnkmail.html - How to Get Rid of Junk Mail, and Telemarketers
http://www.claws-and-paws.com/spam-l/ - Improve your spam-fighting skills
http://www.claws-and-paws.com/spam-l/tracking.html
http://www.coachnet.com/soho__21.htm - Small Office / Home Office Newsletters Anti-Spam Articles for business
http://www.coachnet.com/soho__22.htm
http://www.coachnet.com/soho__29.htm
http://eioMAIL.com/ Spam free web- and POP3-based e-mail account for individual users
http://www.faqs.org/faqs/by-newsgroup/news/news.admin.net-abuse.email.html
http://www.faqs.org/faqs/net-abuse-faq/
http://www.hostedscripts.com/scripts/antispam.html - A script to generate e-mail addresses
http://www.internetwk.com/columns/frezz020199.htm - A good article on why the Internet should be self governing WRT Spam
http://www.junkemail.org/scamspam/ - "Help stop Scam Spammers!"
http://www.kclink.com/spam/ - A fight to bill Spammers
http://www.looksmart.com/eus1/eus53832/eus53833/eus225492/eus282819/eus278700/r?l&igv& - Spam link list
http://www.nags.org/
http://groups.yahoo.com/group/anti-spam/join - Anti-Spam mailing list
http://www.petemoss.com/
http://fravia.anticrack.de/enemy.htm - Stalking the spammer Enemy
http://www.robertgraham.com/ - Infosec / computer security page
http://spamsites.org - Where spammers get their software
http://ars.userfriendly.org/cartoons/?id=19990226 - A computer contemplates spam (see http://www.userfriendly.org/static )
http://www.spamcon.org/ - Resources to help Recipients, Marketers, Sysadmins and Legal pros
http://www.stopspam.org/email/headers/headers.html - More Reading Headers
http://www.usenet2.org/ - A Usenet with no Spam
http://www4.zdnet.com/anchordesk/story/story_index_19970819.html - Special Spam Fighting Edition
http://crash.ihug.co.nz/~bryanc/ - Mac WhatRoute
http://eddie.cis.uoguelph.ca/~tburgess/local/spam.html
http://members.aol.com/emailfaq/emailfaq.html
http://members.aol.com/emailfaq/resource-list.html
http://www.private.org.il/yanig.html - Also yet another newbie guide
http://groups.google.com/groups?selm=36811bc9.1386301459%40news.alt.net - Forgery FAQ
http://www.private.org.il/harvest.html - How spammers get your E-Mail address
http://www.elsop.com/wrc/nospam.htm
http://www.exit109.com/~jeremy/news/antispam.html - Spam Software
http://donotcall.gov/ - or call 1-888-382-1222 - Put yourself on the national "Do Not Call" list
http://www.rahul.net/falk/index.html#howtos
http://www.river.com/users/share/cluetrain/ - My mailbox. My property. My personal space. My rules. Deal with it.
http://www.spam-archive.org/ - A collection of email-Spams.
http://www.webfoot.com/advice/email.biblio.html - General E-Mail info
http://www.winsite.com/win3/winsock/page6.html - Windows Internet Utilities
http://www.winsite.com/win95/netutil/index.html - Win 95 Net Utils
http://www.winsite.com/win95/netutil/page11.html - netcop / netlab95.zip
Spam Info in other languages:
http://cwisdb.cc.kuleuven.ac.be/pisa/nl/spam.htm - Netherlands
http://member.nifty.ne.jp/usr/negi/news.html - Japan
http://member.nifty.ne.jp/usr/negi/newsgroup0.html - Japan
http://perso.magic.fr/roumazeilles/spamantf.htm - Spam Anti! French
http://portale.web.de/Internet/Spam/ - German Anti-Spam
http://www.despaml.interrob.de/ - German Anti-Spam Mailing List
http://www.euro.cauce.org/ - Many languages
http://www.euro.cauce.org/en/index.html - English
http://www.nextel.no/kundesenter/hjelp/guider/901645506.5885.html - Norway
http://www.online-recht.de/vorent.html?LGBerlin980514 - German Anti-Spam and costs
http://www.snafu.de/~laura/de.admin.net-abuse.mail.txt - German net abuse FAQ
Translate from/to English French, German, Spanish, Portuguese, Italian (etc.)
http://babel.altavista.com/translate.dyn
or
English to French:
http://translate.google.com/translate?hl=fr&sl=en&u=http://digital.net/~gandalf/spamfaq.html
English to German:
http://translate.google.com/translate?hl=de&sl=en&u=http://digital.net/~gandalf/spamfaq.html
English to Italian:
http://translate.google.com/translate?hl=it&sl=en&u=http://digital.net/~gandalf/spamfaq.html
English to Spanish:
http://translate.google.com/translate?hl=es&sl=en&u=http://digital.net/~gandalf/spamfaq.html
Or why Netabuse is bad :
http://cnn.com/TECH/computing/9808/10/tastyspam.idg/
http://www.csoonline.com/alarmed/06192003.html - Is someone watching everything you type?
http://www.fraudbureau.com/articles/consumer/article14.html - The cost of spam
http://www.honet.com/Nadine/permission.htm - Why permission is needed to send e-mail
http://www.honet.com/Nadine/default.htm - Someone types in a bad e-mail address and an innocent party starts getting spam
http://www.honet.com/Nadine/Unsubscribe.htm - Why Unsubscribe doesn't work
http://www.infoworld.com/article/03/03/14/11winman_1.html - Microsoft Update --> Watch what your computer sends out
http://www.infoworld.com/article/03/04/15/HNaolspammers_1.html - AOL takes spammers to court
http://www.infoworld.com/article/03/04/10/hnspamgov_1.html - US Government "Can spam" bill.
http://www.nwfusion.com/news/2001/0104spamspam.html - Time and cost of SPAM
http://www.nwfusion.com/news/2001/0104spambust.html - Two busted for Spam fraud / envelope stuffing
http://www.nwfusion.com/columnists/2001/0416gibbs.html - ?Logic? of a spammer and why (if everybody did it) you would get 1,370 e-mails per hour
http://www.nwfusion.com/research/2002/0513spam.html - How spam brings down servers
http://www.nwfusion.com/research/2002/0513spamside4.html - How spammers get your e-mail address
http://www.nwfusion.com/newsletters/sec/2002/01331360.html - Scumware, unauthorized software additions to your computer
http://www.nwfusion.com/newsletters/sec/2002/01366115.html - Scumware prevention and removal
http://www.nwfusion.com/news/2003/0224spammers.html?net - Spammers using students to send spam
http://www.nwfusion.com/news/2003/0224spammerside.html - Spam driving - Why wireless is bad
http://www.nwfusion.com/news/2003/0227spamspam.html?net  - Corporate spam tools
http://www.nwfusion.com/newsletters/sec/2003/0303sec2.html - Security for those that aren't computer security geeks
http://www.nwfusion.com/columnists/2003/0630backspin.html - What spam really costs Part I
http://www.nwfusion.com/columnists/2003/0707backspin.html - What spam really costs Part II
http://enterprisesecurity.symantec.com/content.cfm?articleID=1369 - The cost of Spam (at bottom of article) and how spammers are trying to fight back
Protecting your reputation in Cyberspace - How To / How Not To communicate on the Internet:
http://www.nwfusion.com/newsletters/sec/2001/00322091.html - Part 1
http://www.nwfusion.com/newsletters/sec/2001/00380626.html - Part 2
http://www.nwfusion.com/newsletters/sec/2001/00408507.html - Part 3 - Why not to spam
http://www.nwfusion.com/newsletters/sec/2001/00408551.html - Part 4
http://www.nwfusion.com/newsletters/sec/2001/00450966.html - Part 5
http://www.nwfusion.com/newsletters/sec/2001/00477475.html - Part 6
http://www.nwfusion.com/newsletters/sec/2001/00519056.html - Part 7
http://www.nwfusion.com/newsletters/sec/2001/00477474.html - How Not To Send Out An "Alert"
http://www.nwfusion.com/news/2003/0415aolwield.html?net - AOL wields legal, technical weapons in spam war
Spammers / Spyware Convictions:
http://www.informationweek.com/shared/printableArticle.jhtml?articleID=52601698
http://australianit.news.com.au/articles/0,7204,11319598%5E15331%5E%5Enbv%5E15306%2D15318,00.html
http://www.infoworld.com/article/04/10/08/HNftcspyware_1.html
http://www.reuters.com/newsArticle.jhtml?storyID=6455433
http://www.computerworld.com/newsletter/0,4902,96528,00.html?nlid=PM

http://www.securityfocus.com/news/4217 - Spammers sending out Trojan Programs to turn home computers into spamming machines
http://www.nwfusion.com/news/2005/0125spambust.html?net - Spam busters go on the offensive
First register at: http://www.sensepost.com/garage_portal.html to look at: http://www.sensepost.com/restricted/ISSA2004_spam_paper.pdf - How Spammers Work
Listen to The Spam Avenger abuse spammers - http://www.thespamavenger.com/
Equal time, The spammer's viewpoint (Why Spam is good):
http://www.juicycerebellum.com/spam.htm
http://listen.to/spammers - Spammers Speak
http://groups.google.com/groups?selm=7iviu5%2475g%241%40bgtnsc03.worldnet.att.net - Gerald Kohler ( gkohler@worldnet.att.net ) argues for spam, with some good rebuttals. Click on "Thread" then click on message 8 then click on next in thread to follow the conversation.
Opinions from one spammer (I wouldn't trust much of what is said in these pages if anything at all ...):
http://www.marketing-2000.net/
http://www.freep.com/money/tech/mwend6_20021206.htm - Spammers don't like spam :-)
http://www.marketing-2000.net/legal.htm - Bulk E-Mail - Is It Legal? This page *used* to say "Many of these anti-spammer extremists do not have regular jobs" (Hmm ... I guess my 50+ hour a week high tech job doesn't count?)
http://www.marketing-2000.net/survpage.htm - Bulk E-Mail Marketing guide
http://www.marketing-2000.net/testimonials.htm - Testimonies
Of course feel free to send your comments to escalate@marketing-2000.net concerns@marketing-2000.net or questions@marketing-2000.net
What the alt.binaries.slack Organization has done to fight Spam :
http://www.sputum.com/spit/Main.htm
And the Alt.Gothic Special Forces:
http://thingy.apana.org.au/~fun/agsf/
http://www.izzy.net/~jfron/agsf/tools/
AGSF FAQ:
http://www.legendsmagazine.net/pan/panstuff/agsffaq.htm


Disclaimer :
I am not a lawyer and this is not legal advice. For legal advice, consult an attorney with appropriate expertise in this area of the law who is licensed to practice in your jurisdiction.

80% of the Internet is bull, free advice is worth every penny you paid for it :-).  Brought to you via News since November 1995.

------------------------------------------------------------------
Do not meddle in the affairs of wizards for they are subtle and
quick to anger.
Ken Hollis - Gandalf The White - gandalf@digital.net - O- TINLC
WWW Page - http://gandalf.home.digital.net/
Trace E-Mail forgery - http://gandalf.home.digital.net/spamfaq.html
Trolls crossposts  - http://gandalf.home.digital.net/trollfaq.html

p3scan-2.3.2/scanner_clamd.c0000644000175000001440000002514510347310635014511 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "p3scan.h" #include "getlinep3.h" #define DEFAULT_SERVER "127.0.0.1" #define DEFAULT_PORT "3310" #define COMMAND "STREAM\r\n" #undef DEBUG_MESSAGE struct configuration_t * config; extern char *substr(char *string, int start, size_t count); extern int checktimeout(struct proxycontext *p); extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); int check(struct proxycontext *p, char * filetoscan, char ** virname){ int pri_socket=0,sec_socket=0; int ret=0, sendret=0; int buf_len=0; int mailfd=0; unsigned long len=0; int loc,loc2; char *tmp=NULL; char buf[256]=""; char *buf_c; char *port=NULL; #define VISIZE 1000 char *vi=w_malloc(VISIZE); struct sockaddr_in clamd_pri, clamd_sec; struct linebuf *filebuf; if (vi==NULL) return SCANNER_RET_ERR; /* Create socket */ pri_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (pri_socket == -1){ do_log(LOG_CRIT, "Could not create socket!"); return SCANNER_RET_ERR; } if (!strncmp(config->clamdport,"110",3) || atol(config->clamdport)==config->sslport){ /* TODO: Why is this variable getting blown away when "useurl" is enabled? */ tmp=strchr(config->virusscanner,':'); loc = tmp-config->virusscanner; loc2 = tmp-config->virusscanner+1; len=strlen(config->virusscanner); config->clamdport=substr(config->virusscanner,loc2,len); config->clamdserver=strndup(config->virusscanner,loc); //TODO: 6 bytes in 1 blocks are definitely lost in loss record 1 of 2 do_log(LOG_DEBUG, "Reset ClamdServer to: %s Port: %s",config->clamdserver, config->clamdport); } bzero(&clamd_pri, sizeof(clamd_pri)); clamd_pri.sin_family = AF_INET; clamd_pri.sin_addr.s_addr = inet_addr(config->clamdserver); clamd_pri.sin_port = htons(strtoul(config->clamdport, NULL, 10)); /* Connect to server */ ret = connect(pri_socket,(struct sockaddr *) &clamd_pri, sizeof(clamd_pri)); if (ret != 0){ do_log(LOG_CRIT, "Could not connect!"); close(pri_socket); return SCANNER_RET_ERR; } /* Send "STREAM" command */ if (write(pri_socket,COMMAND,strlen(COMMAND))==-1){ do_log(LOG_DEBUG, "write(): %s", strerror(errno)); do_log(LOG_CRIT,"write(): %s", strerror(errno)); close(pri_socket); return EX_IOERR; // errno: 74 } /* Read server response to get new port number */ if ((buf_len=read(pri_socket,buf,sizeof(buf)-1))==-1){ do_log(LOG_DEBUG, "read(): %s", strerror(errno)); do_log(LOG_CRIT,"read(): %s", strerror(errno)); return EX_IOERR; } /* Parse port number from "PORT XX..." */ buf[buf_len] = 0; if (strncasecmp(buf, "PORT ", sizeof("PORT ") -1) != 0) { do_log(LOG_DEBUG, "could not get port!"); close(pri_socket); return EX_PROTOCOL; // errno: 76 } port = buf + sizeof("PORT ") -1; while(*port == ' ') port ++; /* Create new socket */ bzero(&clamd_sec, sizeof(clamd_sec)); clamd_sec.sin_family = AF_INET; clamd_sec.sin_addr.s_addr = inet_addr(config->clamdserver); clamd_sec.sin_port = htons(strtoul(port, NULL, 10)); sec_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (sec_socket == -1){ do_log(LOG_CRIT, "Could not create secondary socket!"); close(pri_socket); return SCANNER_RET_ERR; } /* Connect to new port */ ret = connect(sec_socket,(struct sockaddr *) &clamd_sec, sizeof(clamd_sec)); if (ret != 0){ do_log(LOG_CRIT, "Could not connect secondary!"); close(pri_socket); close(sec_socket); return SCANNER_RET_ERR; } // Send file: filebuf=linebuf_init(16384); if ((mailfd=open(filetoscan, O_RDONLY ))<0){ close(pri_socket); close(sec_socket); linebuf_uninit(filebuf); do_log(LOG_EMERG, "Can't open mailfile (%s)!\n", filetoscan); return SCANNER_RET_ERR; } while (1){ ret=checktimeout(p); if (ret < 0){ close(pri_socket); close(sec_socket); linebuf_uninit(filebuf); close(mailfd); return SCANNER_RET_CRIT; } if ((ret=getlinep3(mailfd, filebuf))<0){ if (ret==GETLINE_TOO_LONG){ /* Buffer contains part of line, take care of later */ } else { /* Other error, take care of later */ break; } } if (filebuf->linelen >=0 ){ len += filebuf->linelen; #ifdef DEBUG_MESSAGE do_log(LOG_DEBUG, ">%s", filebuf->line); #endif /* Take care of buffer here */ if (ret==GETLINE_TOO_LONG){ sendret=writeline(sec_socket, WRITELINE_LEADING_NONE, filebuf->line); } else { sendret=writeline(sec_socket, WRITELINE_LEADING_RN, filebuf->line); } if (sendret==GETLINE_PIPE){ do_log(LOG_CRIT, "Clam Server disappeared!"); close(pri_socket); close(sec_socket); linebuf_uninit(filebuf); close(mailfd); return EPIPE; } else if (sendret){ linebuf_uninit(filebuf); close(pri_socket); close(sec_socket); close(mailfd); do_log(LOG_EMERG, "Error sending mail to clamd!"); /* we are dead now. Should not reach here. But allow it to fall through in case LOG_EMERG is changed in the future. */ return SCANNER_RET_ERR; } } } linebuf_uninit(filebuf); close(mailfd); /* Close secondary socket to force primary socket output */ close(sec_socket); if (ret!=GETLINE_EOF){ do_log(LOG_CRIT, "error reading from mailfile %s, error code: %d", filetoscan, ret); close(pri_socket); return SCANNER_RET_ERR; } /* Get response from primary socket */ if ((buf_len=read(pri_socket,buf,sizeof(buf)-1))==-1){ do_log(LOG_DEBUG, "read(): %s", strerror(errno)); do_log(LOG_CRIT,"read(): %s", strerror(errno)); close(pri_socket); return EX_IOERR; } /* If response contains "FOUND" then there is a virus */ // "stream: Eicar-Test-Signature FOUND" /* If response contains "OK" then there is no virus */ // "stream: OK" /* Parse Virusname */ close(pri_socket); buf[buf_len] = 0; buf_c = buf + buf_len; while(*buf_c == '\r' || *buf_c == ' ') *buf_c --; if (buf_c - buf >= sizeof("FOUND") && strncasecmp(buf_c - sizeof("FOUND"), "FOUND", sizeof("FOUND")-1) == 0) { char *buf_s = buf; buf_c -= sizeof("FOUND"); if (strncasecmp(buf_s, "stream:", sizeof("stream:")-1) == 0) { buf_s += sizeof("stream:")-1; while(*buf_s == ' ') buf_s ++; snprintf(vi,(int)(buf_c - buf_s),"%s",buf_s); } } *virname=vi; if (strlen(*virname) >3) return SCANNER_RET_VIRUS; // contains a virus return SCANNER_RET_OK; // all ok, no virus } static int init1(void){ int len,loc,loc2; char *tmp=NULL; if (strlen(NONULL(config->virusscanner))<1){ tmp=strndup(DEFAULT_SERVER,strlen(DEFAULT_SERVER)); strncat(tmp,":",1); strncat(tmp,DEFAULT_PORT,4); config->virusscanner=tmp; do_log(LOG_CRIT, "Clamd init: No scanner was defined. we're using %s",config->virusscanner); } tmp=strchr(config->virusscanner,':'); if (tmp){ loc = tmp-config->virusscanner; loc2 = tmp-config->virusscanner+1; len=strlen(config->virusscanner); config->clamdport=substr(config->virusscanner,loc2,len); config->clamdserver=strndup(config->virusscanner,loc); //TODO: 6 bytes in 1 blocks are definitely lost in loss record 1 of 2 do_log(LOG_DEBUG, "Clamd init. Server: %s Port: %s",config->clamdserver, config->clamdport); }else{ do_log(LOG_CRIT, "Clamd init unable to locate separator: %s",config->virusscanner); return SCANNER_RET_ERR; } if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. scanning completely disabled"); return SCANNER_RET_ERR; } /* bzero(&clamd_pri, sizeof(clamd_pri)); clamd_pri.sin_family = AF_INET; clamd_pri.sin_addr.s_addr = inet_addr(config->clamdserver); clamd_pri.sin_port = htons(strtoul(config->clamdport, NULL, 10)); */ return 0; } static int scan(struct proxycontext *p, char **virname){ int ret; do_log(LOG_DEBUG, "Clamd TCP scanner says hello"); ret = check(p, p->scanthis, virname); do_log(LOG_DEBUG, "Clamd TCP scanner says goodbye: %i",ret); return ret; } static void uninit1(void){ if(!config->virusscanner) free(config->virusscanner); } scanner_t scanner_clamd = { "clamd", /* name */ "ClamAV TCP Daemon", /* description */ &init1, /* init1 (once, afer startup) */ NULL, /* init2 (every connection before first mail) */ &scan, /* scan */ NULL, /* uninit2 */ &uninit1, /* uninit1 */ 0 /* dirscan */ }; p3scan-2.3.2/LICENSE0000644000175000001440000004341310347310635012557 0ustar jlaiusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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. , 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. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. (http://www.openssl.org/support/faq.html#LEGAL2) p3scan-2.3.2/ripmime0000777000175000001440000000000010347310635015316 2ripmime-1.4.0.6ustar jlaiusersp3scan-2.3.2/README0000644000175000001440000003332110347310635012427 0ustar jlaiusersP3Scan v2.2 DESCRIPTION This is a fully transparent proxy-server for POP3, SMTP, and limited POP3S Clients. It runs on a Linux box with iptables (for port re-direction). It can be used to provide email scanning from/to the internet, to/from any internal network and is ideal for helping to protect your "Other OS" LAN from harm, especially when used synergically with a firewall and other Internet Proxy servers. It is designed to enable scanning of email messages for Viruses, Worms, Trojans, Spam (read as "Un-solicited Bulk Email"), and harmfull attachments. Because viewing HTML mail can enable a "Spammer" to validate an email address (via web bugs), it can also provide dangerous HTML tag stripping. OVERVIEW It works typically in situations where the linux box lies between the network you wish to protect and the "outer world". You have to set up a port redirection rule with iptables (see "IP-Tables / Port redirecting") so that all connections from e.g. inside your office to any POP3, POP3S, and SMTP server outside in the world will not leave your router, but come to a local port, on which P3Scan listens. P3Scan receives the original destination address of the "outer world" server from the Linux kernel and connects to that address. Anything received from the client will be sent to the server, and vice versa but with a little enhancement: when a message is sent from the server it's parsed, stored into a file, and scanned. If a virus is found a virus notification is sent instead of the original infected message, which can be quarantined or optionally deleted. If the message is destined for a server and a virus is found, the client will be notified that the server rejected their message due to a virus. The message can also be scanned for spam, have potentially dangerous attachments renamed, and have potentially malicious HTML parts stripped. In the case of incoming messages, this is performed in this order: Attachments are scanned/renamed/deleted (optional) The message is scanned for virus's The message is scanned for SPAM (optional) The message is parsed for web bugs (optional) It should be possible to use any virus scanner using the scannertype=basic configuration option. Known to work with this scanner are FRISK F-Prot and Clam Anti Virus scanners. P3Scan provides other scannertype's for scanning using Kaspersky, Trophie, Clam (TCP) Anti-Virus Daemon's and also a bash script. Any C programmer can easily adapt p3scan for other scan-daemons. Neither the client nor the server has to be configured. None of them will ever notice that there's a mail scanner. From the server point of view the incoming connections are from the linux box IP. From the client point of view the only evidence of the presence of p3scan are virus notifications and some additional X-headers. REQUIREMENTS iptables - Normally installed by default on major Linux Distributions. http://www.netfilter.org libpcre - Normally installed by default on major Linux Distributions. http://www.pcre.org ripmime - Needed if your virus scanner does not support email. http://www.pldaniels.com/ripmime/ An Anti-Virus program P3Scan is known to work with: Kaspersky Anti-Virus for Linux (AVPD) http://www.kaspersky.com/ Trophie Anti-Virus Daemon http://www.vanja.com/tools/trophie/ FRISK F-Prot Antivirus http://www.f-prot.com/ Clam Anti-Virus http://www.clamav.net/ F-Secure Anti-Virus http://www.f-secure.com/ Any other virus scanner that can output it's report to the console (stdout) so that it can be captured with "2>&1". DSPAM (optional) - http://www.nuclearelephant.com/projects/dspam/ Mail::SpamAssassin (optional) - http://www.spamassassin.org renattach (optional) - http://www.pc-tools.net/unix/renattach/ p3pmail (optional) - http://p3scan.sourceforge.net/#p3pmail Kernel: The following kernel-parameters have to be enabled to get P3Scan to work. If you have no clue about kernel-compiling, then here is some good news: Most of Linux Distributions (with kernel 2.4.x or greater) support what we need by default. However, if your distribution does not have it, please read your system documentation on how to add it to your kernel. -CONFIG_NETFILTER=y -CONFIG_IP_NF_IPTABLES=[y/m] -CONFIG_IP_NF_TARGET_REDIRECT=[y/m] COMPILATION Change to the directory where you untar'ed the program and make any system specific changes as needed to the "user options" area of the Makefile, then as user: $ make and as root (su root) # make install The only binary will be copied to /usr/sbin/p3scan. Make install will also copy p3scan.conf and the p3scan-xx.mail files into /etc/p3scan while creating directories /etc/p3scan, /var/run/p3scan, /var/spool/p3scan, /var/spool/p3scan/children, and /var/spool/p3scan/notify. It will also create a symbolic link of /etc/p3scan/p3scan.mail to the language file specified in the Makefile. After this, you must prepare your configuration file as described below. Preparing / Manual Configuration: If you decide not to use "make install" (for example, you don't want gcc on your firewall machine where p3scan is going to run). Please ensure the following directories are created and that they are owned and readable/writable by only the user "mail" (default). As root, create the following directories: # mkdir /etc/p3scan # mkdir /var/run/p3scan # mkdir -p /var/spool/p3scan/children # mkdir -p /var/spool/p3scan/notify Make them "owned" by the user "mail" (default): # chown mail.mail /etc/p3scan # chown mail.mail /var/run/p3scan # chown -R mail.mail /var/spool/p3scan Then make them read/write by only our user: # chmod 700 /etc/p3scan # chmod 700 /var/run/p3scan # chmod -R 700 /var/spool/p3scan If you don't have the user "mail" and do not know how to create this user, please see your operating systems documentation on how to create a user. Change the symlink /etc/p3scan/p3scan.mail if you wish (this points to the template which is sent instead of a virus). If you use any special character (like German-umlauts ) don't forget to set the charset to "utf8" (charset="utf8" vice charset="iso-8859-1"). Also, ensure the leading dot at the end of the file is there and has a carriage return after the dot. CONFIGURATION The configuration file defaults to /etc/p3scan/p3scan.conf This file is a mixture of configuration data and documentation and MUST be modified to work correctly upon initial installation. You must specify AT LEAST how to call your anti-virus program and how to extract the virus name (if one is found). The purpose of p3scan is to provide virus protection to an internal network. So, not having a virus scanner is not an option. That being said, if you still do not want a virus scanner enabled, you can just set the scannertype/scanner to basic/"/usr/bin/cat". Currently, the default configuration is as follows (see p3scan.conf for more info): The PID file is stored in: pidfile = /var/run/p3scan/p3scan.pid The maximum simultaneous scans is: maxchilds = 10 We listen to any address: ip = 0.0.0.0 We listen only on: port = 8110 targetip and targetport are disabled. We run as: user = mail We create notification mails in: notifydir = /var/spool/p3scan/notify before sending. We default to storing infected messages in: virusdir = /var/spool/p3scan We keep infected mail. "justdelete" is not set. We notify only the email client when a virus is detected. "extra" is not set. We use the default /bin/mail to send extra notification email when required. We do not check remaining disk space before scanning mail. "bytesfree" is not set We DO NOT have an Anti-Virus program selected, scanner return code, nor regular expression on how to extract a virus name. scannertype=basic "scanner" is not set "viruscode" is not set "virusregexp" is not set We DO NOT have any "Good" return codes set other than "0" (zero). "goodcode" is not set We DO NOT demime the message or separate attachments from the original mail before scanning. "demime" is not set We DO NOT send entire lines of email header while processing to keep the email client "alive". We send characters instead. "broken" is not set. The default timeout is 30 seconds when processing a large email message. We DO NOT check for email marked as "Spam" by your ISP. "ispspam" is not set. We DO NOT check for "Spam". "checkspam/spamcheck" is not set We DO NOT rename attachments. "renattach" is not pointing to any external program. We DO NOT parse HTML code. "overwrite" is not pointing to any external program. We DO NOT have debug messages being displayed. *"debug" is not set. * Note: The recommended debug procedure is to call p3scan as such: p3scan -d > debug 2>&1 You can then keep track of the debug messages on another terminal with: tail -n 50 -f debug We report all program steps to syslog less debug info. "quiet" is not set. The Virus Report template defaults to /etc/p3scan/p3scan.mail which is a symlink to /etc/p3scan/p3scan-xx.mail generated during "make install", where xx equals the LANG option set in the Makefile. We do not copy the virus template message for the bash scanner to modify. "altvnmsg" is not set. We generate the Virus Report Subject line as: "[Virus] found in a mail to you:" you can change this with the "subject" line in p3scan.conf. We generate the Virus Report file disposition line (when justdelete is set): notify = Per instruction, the message has been deleted. When an outgoing message is rejected, the default of "Virus detected! P3scan rejected message!" is used. Outgoing message size is not checked before parsing. The port for SSL messages is 995. We do not generate Virus Definition data for outgoing/notification messages. "footer" is not set. IP-Tables / Port redirecting: Rules like: iptables -t nat -A PREROUTING -p tcp -i eth0 --dport pop3 -j REDIRECT --to 8110 iptables -t nat -A PREROUTING -p tcp -i eth0 --dport smtp -j REDIRECT --to 8110 iptables -t nat -A PREROUTING -p tcp -i eth0 --dport pop3s -j REDIRECT --to 8110 are enough. Change eth0 to your device for the inbound connections (your office or home network). Also, "pop3", "smtp", and/or "pop3s" must be defined in your "services" file. Normally locate at: /etc/services. If it is not defined, enter the definition you have for 110/tcp. IE: pop-3, or just enter the port number(s). IE: 110, 25, or 995 Spam Checking The spam checking capability of p3scan has only been tested using DSPAM >= 3.0.0-rc2 and Mail::SpamAssassin v2.6 >= v3.0.1 If using dspam, you need to install the program according to the documentation found at http://www.nuclearelephant.com/projects/dspam/ The recommended procedure is the virtual-users interface of the mysql driver. If you will be scanning for spam using SpamAssassin, you need to install the program according to the documentation found at http://www.spamassassin.org The easiest (as fastest) interface to Mail::SpamAssassin is through it's daemon program "spamd" using "spamc". You can start spamd as follows before running p3scan: *Note: This example is for using SpamAssassin w/mysql /usr/bin/spamd -d -u spamd -x -q & SSL Message parsing: We are able to perform limited checking of messages using SSL. To use this feature, you must tell your email client NOT to use SSL and just change the pop3 port from 110 to 995. If p3scan sees a destination port of 995 (or whatever port "sslport" is set to) it will initiate an SSL conversation. NOTE: This is limited support as p3scan will not show you the SSL certificate and will just accept any certificate as sent by the actual server. renattach: Is used to rename attachments and is totally configurable. Renattach must be compiled, installed, and configured before enabling this feature. See the renattach documentation INSTALL and README for further information. HTML Parsing: The HTML parsing option is now an external program to p3scan. This facilitates using any program you can find. I have written a separate program for this function called p3pmail which can be found on the p3scan web site. P3PMail will obfuscate the tags "href" and "src" the two most dangerous HTML tags (IMHO) for email. Of course, if your using a non-html email client, you will not have to worry about "web-bugs". Startup: Call p3scan without any parameters, it will move into the background. You can monitor it's operation via your systems log file. You should also test your installation by sending yourself an eicar test virus (which will not damage your system). You can get versions of this file at http://www.eicar.org/anti_virus_test_file.htm If you think too much information is being sent to your system logs, you can enable the "quiet" option. This will inhibit "normal" messages. If p3scan is started by root, it will change it's user to "mail" (default) after it finishes it's initial startup. If you are using Mail::SpamAssassin, start spamd BEFORE running p3scan. RC System / Boot up: "make install" should determine the correct p3scan startup file and place it in the proper directory. If for some reason this does not happen, you can add p3scan to your normal default startup file. For example, in Slackware place p3scan as follows: # echo "/usr/sbin/p3scan" >> /etc/rc.d/rc.local and please notify the p3scan-main mailing list of this problem. SEE ALSO "man p3scan" "man p3scan_readme" "p3scan.conf" BUGS/SUPPORT Please report any bugs to the p3scan support mailing list accessable through: http://sourceforge.net/projects/p3scan AUTHORS Jack S. Lai and contributers (see CONTRIBUTERS file). p3scan-2.3.2/parsefile.c0000644000175000001440000002047010347310160013657 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ /* * * parsefile.c * * (C) 2002 by Folke Ashberg * * $Id: parsefile.c,v 1.1.1.1 2003/01/23 12:41:40 vtoroman Exp $ * * parsefile.c provides functions for parsing text with replacing keywords. * it uses my getlinep3.c for string handling * * This stuff provides functions for linehandlin on file descriptors, * especially using netork sockets, because the getline function is * non blocking (the write stuff not!) * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include "parsefile.h" #include "getlinep3.h" #include "p3scan.h" extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); #define NONULL(x) ( x==NULL ? "" : x) /* this is nice, found in the mutt code */ struct paramlist * paramlist_init(void){ struct paramlist *pl; pl=malloc(sizeof(struct paramlist)); //--pl=w_malloc(sizeof(struct paramlist)); pl->name=NULL; pl->value=NULL; pl->next=NULL; return pl; } void paramlist_uninit(struct paramlist ** p){ struct paramlist * next; while (*p){ next=(*p)->next; if ((*p)->name) free((*p)->name); if ((*p)->value) free((*p)->value); free(*p); //--if ((*p)->name) w_free((*p)->name); //--if ((*p)->value) w_free((*p)->value); //--w_free(*p); *p=next; } } int paramlist_set(struct paramlist * p, char * name, char * value){ /* TODO: deleteting (value==NULL) does nothing */ struct paramlist *last=NULL; if (!name) return 1; while (p && (p->name) && (strcasecmp(p->name, name))){ last=p; p=p->next; } if (!p){ if (!value) return 0; p=paramlist_init(); if (last) last->next=p; } if (p->name) free(p->name); if (p->value) free(p->value); //--if (p->name) w_free(p->name); //--if (p->value) w_free(p->value); p->name=strdup(name); p->value= value ? strdup(value) : NULL; return 0; }; char * paramlist_get(struct paramlist * params, char * name){ if (!name) return NULL; while (params){ if (params->name && !strcasecmp(params->name, name)) return params->value; params=params->next; } return NULL; } int paramlist_strreplace(char ** dst, char * in, struct paramlist * params){ typedef struct replacelist { int pos; /* position in in where to replace */ int newlen; /* length of new text (params->value) */ int oldlen; /* length of old text (params->name) */ char * new; /* char to replace with */ struct replacelist * next; } replacelist; int len, pos, l; int replacecount=0; char * occ; char * in_inc; int lastreplacepos; int incremented; struct replacelist * rl_first = NULL, * rl = NULL, * rl_last, *rl_next; struct paramlist * p; if (!in) return -1; if (!params){ /* nothing to replace */ *dst=in; return 0; } *dst=NULL; len=strlen(in); p=params; /* look for replacements and save them, count size of new string */ while (p){ if (p->name){ in_inc=in; incremented=0; while ((occ=strstr(in_inc, p->name))!=NULL){ pos=occ - in; /* add entry to replacelist, which has to be sorted by pos, * we have to look up our entry point */ rl=rl_first; rl_last=NULL; while (rl){ if (rl->pos > pos) break; rl_last=rl; rl=rl->next; } rl_next=rl; rl=malloc(sizeof(struct replacelist)); //--rl=w_malloc(sizeof(struct replacelist)); if (rl_last) rl_last->next=rl; else rl_first=rl; rl->next=rl_next; rl->pos=pos; rl->newlen=strlen(NONULL(p->value)); rl->new=p->value; rl->oldlen=strlen(p->name); len+=rl->newlen - rl->oldlen; replacecount++; /* override old string to ensure no further shorter * string will match */ memset(occ, 1, strlen(p->name)); /* we loop strstr until we cannot find this p->name */ in_inc+=rl->oldlen; incremented+=pos; } } p=p->next; } if (replacecount>0){ *dst=malloc(len+1); //--*dst=w_malloc(len+1); (*dst)[len]='\0'; rl=rl_first; pos=0; lastreplacepos=0; while (rl){ /* copy unreplaced text into buf */ l=rl->pos - lastreplacepos; memcpy(&(*dst)[pos], &in[lastreplacepos], l); lastreplacepos=rl->pos + rl->oldlen; pos+=l; if (rl->newlen > 0){ /* copy replaced text */ memcpy(&(*dst)[pos], rl->new, rl->newlen); pos+=rl->newlen; } rl=rl->next; } /* copy the rest of the line */ if ((l=len-pos)>0) memcpy(&(*dst)[pos], &in[lastreplacepos], len-pos); } else *dst=in; rl=rl_first; while (rl){ rl_last=rl; rl=rl->next; free(rl_last); //--w_free(rl_last); } return replacecount; } int parsefile(char * infile, char * outfile , struct paramlist * params, int leading){ int in, out, ret; //if ( (in=open(infile, O_RDONLY))<0) return 1; if ( (in=open(infile, O_RDONLY))<0) return -1; /** if file already exists just send that file and don't do any parsing * prim vt */ if ( (out=open(outfile,O_RDONLY))>=0){ close(in); close(out); return 0; } if ( (out=open(outfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR ))<0) return 1; ret=parsefds(in, out, params, leading); close(in); close(out); return ret; } int parsefds(int in, int out , struct paramlist * params, int leading){ int res, replacements; char * replaced; struct linebuf *l; if ((l=linebuf_init(4096))==NULL) return 1; while ( (res=getlinep3(in, l))>=0){ if (l->linelen >=0 ){ replacements=paramlist_strreplace(&replaced, l->line, params); if (writeline(out, leading, replaced)){ //error } if (replacements>0) free(replaced); //--if (replacements>0) w_free(replaced); } } linebuf_uninit(l); if (res!=GETLINE_EOF) return 1; return 0; } p3scan-2.3.2/parsefile.h0000644000175000001440000000712510347310160013666 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ /* * parsefile.h * (C) 2002 by Folke Ashberg * * $Id: parsefile.h,v 1.4 2002/06/11 23:09:16 folke Exp $ * * parsefile.c provides functions for parsing text with replacing keywords. * it uses my getline.c for string handling * * This stuff provides functions for linehandlin on file descriptors, * especially using netork sockets, because the getline function is * non blocking (the write stuff not!) * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _PARSEFILE_H #define _PARSEFILE_H typedef struct paramlist { char * name; char * value; struct paramlist * next; } paramlist; /* parses infile to outfile, all words given in params will be replaced * leading (\r\n or \n) is one of WRITELINE_LEADING_[NONE|N|RN] */ int parsefile(char * infile, char * outfile, paramlist * params, int leading); /* parses file descriptor in to out, all words given in params will be replaced * leading (\r\n or \n) is one of WRITELINE_LEADING_[NONE|N|RN] */ int parsefds(int in, int out , paramlist * params, int leading); /* Adds/Updates name to paramlist. * To delete a name, call it with value set to NULL. */ int paramlist_set(struct paramlist * params, char * name, char * value); /* returnes the value of name */ char * paramlist_get(struct paramlist * params, char * name); /* initialize paramlist */ struct paramlist * paramlist_init(void); /* unitializes the paramlist */ void paramlist_uninit(struct paramlist ** params); #endif /* ifndef _PARSEFILE_H */ p3scan-2.3.2/p3scan.sh0000755000175000001440000000255410347310635013301 0ustar jlaiusers#!/bin/bash # # This bash script is just to show basic interaction w/p3scan # # filename # %MAILFROM% # %MAILTO% # %USERNAME% # %SUBJECT% # %MAILDATE% # %SERVERIP% # %SERVERPORT% # %CLIENTIP% # %CLIENTPORT% # %PROTOCOL% # %PROGNAME% # %VERSION% # %VDINFO% # %HEADER% # OUTPUT="/tmp/p3scan.test" THISPGM= $0 FILENAME= $1 MAILFROM= $2 MAILTO= $3 USERNAME= $4 SUBJECT= $5 MAILDATE= $6 SERVERIP= $7 SERVERPORT= $8 CLIENTIP= $9 CLIENTPORT= ${10} PROTOCOL= ${11} PROGNAME= ${12} VERSION= ${13} VDINFO= ${14} HEADER= ${15} echo "$THISPGM" > $OUTPUT echo "$FILENAME" >> $OUTPUT echo "$MAILFROM" >> $OUTPUT echo "$MAILTO" >> $OUTPUT echo "$USERNAME" >> $OUTPUT echo "$SUBJECT" >> $OUTPUT echo "$MAILDATE" >> $OUTPUT echo "$SERVERIP" >> $OUTPUT echo "$SERVERPORT" >> $OUTPUT echo "$CLIENTIP" >> $OUTPUT echo "$CLIENTPORT" >> $OUTPUT echo "$PROTOCOL" >> $OUTPUT echo "$PROGNAME" >> $OUTPUT echo "$VERSION" >> $OUTPUT echo "$VDINFO" >> $OUTPUT echo "$HEADER" >> $OUTPUT # Call your virus scanner(s) here: #/usr/bin/clamdscan $1 # End of scanner call # # for use with "virusregexp = .*: (.*) FOUND" # # If response contains "FOUND" then there is a virus #echo "Eicar-Test-Signature FOUND" # If response contains "OK" then there is no virus */ echo "OK" # # exit 1 if virus found. exit 0 p3scan-2.3.2/p3scan-init.d0000644000175000001440000000304510347310635014044 0ustar jlaiusers#!/bin/bash # # p3scan startup script # # usage: p3scan { start | stop | restart | status } # # Jack S. Lai - laitcg@cox.net . /etc/sysconfig/init pretty(){ echo -en $CMDTITLE if $CMD 2> /dev/null; then $MOVE_TO_COL echo -en "[" $SETCOLOR_SUCCESS echo -en " OK \c" $SETCOLOR_NORMAL echo "]" else $MOVE_TO_COL echo -en "[" $SETCOLOR_FAILURE echo -en "FAILED\c" $SETCOLOR_NORMAL echo "]" fi } case "$1" in start) # Start p3scan echo -en "Starting p3scan" /usr/sbin/p3scan & $MOVE_TO_COL echo -en "[" $SETCOLOR_SUCCESS echo -en " OK \c" $SETCOLOR_NORMAL echo "]" ;; stop) # Stop p3scan if [ -a /var/run/p3scan/p3scan.pid ]; then CMDTITLE="Stopping p3scan" CMD="kill `cat /var/run/p3scan/p3scan.pid` &>/dev/null" pretty rm -f /var/run/p3scan/p3scan.pid else echo -en "Stopping p3scan" killall p3scan &>/dev/null $MOVE_TO_COL echo -en "[" $SETCOLOR_WARNING echo -en " OK \c" $SETCOLOR_NORMAL echo "]" fi ;; status) # p3scan status if [ -a /var/run/p3scan/p3scan.pid ]; then PID=`cat /var/run/p3scan/p3scan.pid` echo "p3scan OK, running: $PID" else echo "p3scan is stopped" fi ;; restart) $0 stop $0 start ;; *) echo "Usage: p3scan { start | stop | restart | status }" exit 1 esac exit 0 p3scan-2.3.2/getlinep3.c0000644000175000001440000001521210347310160013575 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include #include "getlinep3.h" extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); int secure_write(int fd, char * buf, int len){ /* writes len bytes from buf to fd */ /* repeats if fd can't send len at once */ /* returns error or number of bytes written */ int written=0; int i; if (len==0) return GETLINE_OK; /* that's ok */ if (len<1) return GETLINE_ERR; /* grmpf, that's not */ while ((i=write(fd, &buf[written], len-written))lineend == l->bufend){ /* no --> read some chars */ maxread=l->max -1; if (maxread<1) return GETLINE_TOO_LONG; if (select_fd_read(fd)<1) return GETLINE_NEED_READ; len=read(fd, l->buf, maxread ); if (len<1) return GETLINE_EOF; l->len=len; l->line=l->buf; l->bufend=&(l->buf[len-1]); } else { /* move next line (from which we have already some data) to beginning of buf */ if (l->lineend && l->bufend){ len=l->bufend - l->lineend; memmove(l->buf, l->lineend +1, len ); l->line=l->buf; l->bufend=l->buf + len -1 ; l->len=len; } else { /* there is a not yet finished line in buf */ maxread=l->max - l->len -1; if (maxread<1){ /* no more space left in buf */ if (l->len == l->max){ l->linelen=l->max-1; //l->buf[l->max-1]='\0'; } else { l->linelen=l->len-1; //l->buf[len]='\0'; } l->line=l->buf; /* set to zero, then next call will work */ l->len=0; l->lineend=NULL; l->bufend=NULL; return GETLINE_TOO_LONG; } if (select_fd_read(fd)<1) return GETLINE_NEED_READ; len=read(fd, &(l->buf[l->len]), maxread ); if (len<1){ /* EOF */ /* TODO: what's up if last line has no [\r]\n ? */ l->moredata=0; l->linelen=GETLINE_LINE_NULL; l->line=l->buf; l->buf[0]='\0'; return GETLINE_EOF; } l->len+=len; l->line=l->buf; l->bufend=&(l->buf[l->len-1]); } } last_r=0; p=l->buf; while (p <= l->bufend ){ switch (*p) { case '\r': last_r=1; break; case '\n': l->lineend=p; *p='\0'; if (last_r) *(p-1)='\0'; l->linelen=strlen(l->line); l->moredata=(l->lineend != l->bufend); return GETLINE_OK; /* voila, there is a line */ break; default: last_r=0; } p++; } l->lineend=NULL; l->linelen=GETLINE_LINE_INCOMPLETE; l->moredata=0; return GETLINE_NOLINE; /* could not complete a whole line */ } int writeline(int fd, int leading, char * c){ char * out; int len, res; if (!c) return GETLINE_ERR; //--out=malloc(strlen(c)+3); out=w_malloc(strlen(c)+3); switch (leading){ case WRITELINE_LEADING_NONE: len=sprintf(out, "%s", c); break; case WRITELINE_LEADING_N: len=sprintf(out, "%s\n", c); break; case WRITELINE_LEADING_RN: default: len=sprintf(out, "%s\r\n", c); break; } res=secure_write(fd, out, len); //--free(out); w_free(out); return res; /* bytes / GETLINE_ERR / GETLINE_PIPE */ } int writeline_format(int fd, int leading , char * fmt, ...){ char out[4096]; int len, res; va_list az; if (!fmt) return GETLINE_ERR; va_start(az,fmt); len=vsnprintf(out, 4090, fmt, az); switch (leading){ case WRITELINE_LEADING_NONE: break; case WRITELINE_LEADING_N: out[len]='\n'; len++; break; case WRITELINE_LEADING_RN: default: out[len]='\r'; len++; out[len]='\n'; len++; break; } res=secure_write(fd, out, len); return res; /* bytes / GETLINE_ERR */ } struct linebuf * linebuf_init(int len){ struct linebuf * l; //--l=malloc(sizeof(linebuf)); //--if (!l) return NULL; l=w_malloc(sizeof(linebuf)); //--l->buf=malloc(len); l->buf=w_malloc(len); l->max=len; linebuf_zero(l); return l; } void linebuf_zero(struct linebuf *l){ l->lineend=NULL; l->bufend=NULL; l->len=0; l->linelen=GETLINE_LINE_NULL; l->line=NULL; } void linebuf_uninit(struct linebuf *l){ if (l==NULL) return; //--if (l->buf!=NULL) free(l->buf); //--free(l); if (l->buf!=NULL) w_free(l->buf); w_free(l); l=NULL; } p3scan-2.3.2/getlinep3.h0000644000175000001440000001045310347310160013604 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #ifndef _GETLINE_H #define _GETLINE_H /* return values */ #define GETLINE_OK 0 /* ok, there is a line */ #define GETLINE_NOLINE 1 /* there's no complete line*/ #define GETLINE_NEED_READ 2 /* we need to read from int (but select returned < 0 ) */ #define GETLINE_EOF -1 /* eof */ #define GETLINE_ERR -2 /* an error occured */ #define GETLINE_TOO_LONG -3 /* line is too long for buf */ #define GETLINE_PIPE -4 /* Socket went away */ /* writeline[_*] \r / \r\n control */ #define WRITELINE_LEADING_NONE 0 /* no lading characters */ #define WRITELINE_LEADING_N 1 /* leading \n */ #define WRITELINE_LEADING_RN 2 /* leading \r\n */ /* values of linelen */ #define GETLINE_LINE_INCOMPLETE -1 /* line is not complete */ #define GETLINE_LINE_NULL -2 /* line is NULL (not zero-line) */ typedef struct linebuf { /* these values can be touched if you need */ char *line; /* where the line starts */ int linelen; /* strlen of line, -1 if not complete */ /* do not touch these values, we need it */ char *buf; /* buffer for data */ int max; /* malloced size for buf */ int len; /* size which is in buffer */ char *lineend; /* pointer to end of line (including [\r]\n */ char *bufend; /* pointer to last data char in buf */ int moredata; /* is 1 when there is more data then line */ } linebuf; /* reads a line from fd or l->buf is there any * this function is non blocking. * return values: * GETLINE_OK : there is a (complete line) * GETLINE_NOLINE : we've received data, but no complete line * GETLINE_NEED_READ : we need to read, but select returned no data * GETLINE_EOF : fd is EOF * GETLINE_TOO_LONG : The line can't be retrieved because buf is too * small. The (incomplete) line is accessable! * You can call getline again, but it will return * the rest of that line and not the next one! */ int getlinep3(int fd, struct linebuf * l); /* writes line to fd * set WRITELINE_LEADING_[NONE|N|RN] in leading */ int writeline(int fd, int leading, char * c); /* writes a formatted line to fd * set WRITELINE_LEADING_[NONE|N|RN] in leading */ int writeline_format(int fd, int leading, char * fmt, ...); /* write len bytes of buf to fd * if fd can't receive whole len at once, write repeats * until all is sent */ int secure_write(int fd, char * buf, int len); /* mallocs and zeroes a linebuf * buf should be len bytes long */ struct linebuf * linebuf_init(int len); /* set all values from l to zero * any data which are in buf (also unseen lines) * will be lost */ void linebuf_zero(struct linebuf * l); /* frees l */ void linebuf_uninit(struct linebuf * l); /* runs select (read) on fd with a timeval of 0 (not NULL!) */ int select_fd_read(int fd); #endif /* ifndef _GETLINE_H */ p3scan-2.3.2/p3scan-ru.mail0000644000175000001440000000245510347310635014232 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="utf8" Здравствуйте, %USERNAME%. Данное уведомление создано автоматичски программой P3Scan, которая выполнятся на %HOSTNAME%.%DOMAINNAME% и проверяет всю получаемую почту на вирусы. Отправленное Вам письмо содержало ВИРУС! Данное сообщение получено Вами вместо зараженного. Заголовки оригинального сообщения сохранены, т.ч. Вы можете получить из них дополнительную информацию о первоначальном сообщении. Найденный вирус: %VIRUSNAME% Заявленный отправитель (часто не соотв. действительности): %MAILFROM% Кому: %MAILTO% Дата: %MAILDATE% Тема: %SUBJECT% Данные получения: Протокол %PROTOCOL% клиент %CLIENTIP%:%CLIENTPORT% сервер %SERVERIP%:%SERVERPORT% Файл сообщения: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% Разработчик Jack S. Lai . p3scan-2.3.2/p3scan-en.mail0000644000175000001440000000154310347310635014203 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Hello %USERNAME%. This message body was generated automatically from P3Scan, which runs on %HOSTNAME%.%DOMAINNAME% for scanning all incoming email. It replaces the body of a message sent to you that contained a VIRUS! Instead of the infected email this message has been sent to you. You may look at the message header of this message for the complete email header information of the infected message. Virus name: %VIRUSNAME% (Supposed) Sender of the email: %MAILFROM% Sent To: %MAILTO% On Date: %MAILDATE% Subject: %SUBJECT% Connection data: %PROTOCOL% from %CLIENTIP%:%CLIENTPORT% to %SERVERIP%:%SERVERPORT% Message File: %P3SCANID% Virus Definition Info: %VDINFO% -- %PROGNAME% %VERSION% by Jack S. Lai . p3scan-2.3.2/p3scan-pt-br.mail0000644000175000001440000000150710347310635014625 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Ola %USERNAME%. Esta mensagem foi gerada automaticamente pelo P3Scan, que esta rodando no %HOSTNAME%.%DOMAINNAME% verificando todos os e-mail de entrada. Ele substitui o e-mail enviado que contem um VIRUS ! Em vez me mandar o e-mail infectado, esta sendo enviado este e-mail a voce. Voce pode olhar o cabecalho desta mensagem para uma informacao detalhada do e-mail infectado que foi-lhe enviado. Nome do Virus: %VIRUSNAME% Enviado por(Suposto): %MAILFROM% Enviado para: %MAILTO% Data: %MAILDATE% Assunto: %SUBJECT% Info da Conexao: %PROTOCOL% from %CLIENTIP%:%CLIENTPORT% to %SERVERIP%:%SERVERPORT% Mensagem no Arquivo: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% por Jack S. Lai . p3scan-2.3.2/rc.p3scan0000644000175000001440000000206610347310635013266 0ustar jlaiusers#!/bin/bash # # POP3 Virus Scanner startup script # # usage: p3scan { start | stop | restart | status } # # Jaime Nebrera - jnebrera@eneotecnologia.com # Director de Organizacion - ENEO Tecnologia case "$1" in start) # Start p3scan /usr/sbin/p3scan echo "P3Scan initiated" ;; stop) # Stop p3scan if [ -a /var/run/p3scan/p3scan.pid ]; then kill `cat /var/run/p3scan/p3scan.pid` &>/dev/null rm -f /var/run/p3scan/p3scan.pid echo "P3Scan terminated" else echo "Error: cannot find p3scan.pid" echo "Killing any p3scan daemon in the system ..." echo killall p3scan &>/dev/null fi ;; status) # P3Scan status if [ -a /var/run/p3scan/p3scan.pid ]; then PID=`cat /var/run/p3scan/p3scan.pid` echo "P3Scan OK, running: $PID" else echo "P3Scan is stopped" fi ;; restart) $0 stop $0 start ;; *) echo "Usage: p3scan { start | stop | restart | status }" exit 1 esac exit 0 p3scan-2.3.2/ripmime-1.4.0.6/0000755000175000001440000000000010347310160014002 5ustar jlaiusersp3scan-2.3.2/ripmime-1.4.0.6/TODO0000644000175000001440000000171210347164530014503 0ustar jlaiusers--ripMIME 1.3.x----------TO DO LIST--------------- - 16 July 2003 - Make ripMIME work with memory streams rather than files. - 16 March 2003 - Create a way for users to explicitly define what filename the contents of the 'blankzone' will be saved to, rather than [typically] textfile0. - Move all decoding operations ( UUdecode, BASE64 ) into a library called libmime-decoders --ripMIME 1.2.x----------TO DO LIST--------------- This list is for anyone who wishes to help out with the ripMIME development, and is wondering what there needs to be done. I'm always looking out for mailpacks which fail to extract correctly with ripMIME. Send them along to mailpacks@pldaniels.com gzip'd or bzip2'd. Don't forget to describe what aspect about the mailpack is not unpacking correctly, otherwise I might try the mailpack and see all the files come out and think that there's nothing wrong. Regards. --ripMIME 1.2.x----------TO DO LIST--------------- p3scan-2.3.2/ripmime-1.4.0.6/tnef/0000755000175000001440000000000010347164530014746 5ustar jlaiusersp3scan-2.3.2/ripmime-1.4.0.6/tnef/Makefile0000644000175000001440000000063610347164530016413 0ustar jlaiusers # Possible platforms are SUN, HPUX, DEC, SGI, AIX, Linux and MSDOS # The main definitions are for 32/16 bits and for byte order, the # default is big endian. You can do -D__TNEF_BYTE_ORDER 1234 for little # endian PLATFORM=-D___TNEF_BYTE_ORDER=4321 CFLAGS=-Wall -g -O2 -I. default: config.h tnef.h tnef.c $(CC) $(CFLAGS) $(PLATFORM) -c tnef.c lib: tnef.o ar ruvs tnef.a tnef.o clean: rm -f *.o *.~[ch] p3scan-2.3.2/ripmime-1.4.0.6/tnef/ms-tnef0000644000175000001440000000201710347164530016242 0ustar jlaiusersx>"   0 ;+nTblong@uiuc.eduSMTPblong@uiuc.edu0SMTP0blong@uiuc.edu 0'blong@uiuc.edu' 0SMTP:BLONG@UIUC.EDU9 @:)IPM.Microsoft Mail.Note1<AAAAAAAAAAAAARRRRRRRRRGGGGGGGGGGGGHHHHHHHHHH!!!!!!!!!!!!!!!B  '  ;V !4285420C43D3D011B6570020AFA1A003 #& )6@9 qgp<AAAAAAAAAAAAARRRRRRRRRGGGGGGGGGGGGHHHHHHHHHH!!!!!!!!!!!!!!!qgq BCCжW  SMTP noahban@dvsweb.com;}WHATHASHAPPENEDTOUS? LZFuPI P ch set272prBqstem34E5m}   `ng103P a 4  !w@ pp to us? `.c@ a&@0Gg@0Gg=np3scan-2.3.2/ripmime-1.4.0.6/tnef/config.h0000644000175000001440000000635210347164530016372 0ustar jlaiusers/*************************************************************************** * * config.h for tnef decoder by Brandon Long * Based on config.h from S3MOD by Dan Marks and David Jeske * * (C) 1994,1995 By Daniel Marks and David Jeske * * While we retain the copyright to this code, this source code is FREE. * You may use it in any way you wish, in any product you wish. You may * NOT steal the copyright for this code from us. * * We respectfully ask that you email one of us, if possible, if you * produce something significant with this code, or if you have any bug * fixes to contribute. We also request that you give credit where * credit is due if you include part of this code in a program of your own. * *************************************************************************** * * config.h - compile time configuration options and system specific defines * */ #ifndef _CONFIG_H #define _CONFIG_H 1 /***************************************************************************/ /* The following are system specific settings */ /***************************************************************************/ #if defined(SUN) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined (HPUX) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined(DEC) #undef NEAR_FAR_PTR #elif defined(__sgi) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined(AIX) #undef NEAR_FAR_PTR #define ___TNEF_BYTE_ORDER 4321 #define BIT_32 #elif defined(LINUX) #define BIT_32 #undef NEAR_FAR_PTR #elif defined(MSDOS) #define NEAR_FAR_PTR #undef BIT_32 #else #undef NEAR_FAR_PTR #define BIT_32 #endif /* OS/MACH TYPE */ /***************************************************************************/ /* 16/32 Bit and Byte Order hacks */ /***************************************************************************/ #ifdef BIT_32 typedef short int int16; typedef unsigned short int uint16; typedef int int32; typedef unsigned int uint32; typedef char int8; typedef unsigned char uint8; #else typedef int int16; typedef unsigned int uint16; typedef long int int32; typedef unsigned long int uint32; typedef char int8; typedef unsigned char uint8; #endif /* BIT_32 */ #ifndef WIN32_TYPES #define ULONG uint32 #define SCODE uint32 #define FAR #define LPVOID void * #define WORD uint16 #define DWORD uint32 #define LONG int32 #define BYTE uint8 #endif /* !WIN32_TYPES */ #define endian_switch(x) (((((uint16)(x)) & 0xFF00) >> 8) | \ ((((uint16)(x)) & 0xFF) << 8)) #define long_endian_switch(x) ( ((((uint32)(x)) & 0xFF00UL) << 8) | \ ((((uint32)(x)) & 0xFFUL) << 24) | \ ((((uint32)(x)) & 0xFF0000UL) >> 8) | \ ((((uint32)(x)) & 0xFF000000UL) >> 24)) #if ___TNEF_BYTE_ORDER == 4321 #define big_endian(x) (x) #define long_big_endian(x) (x) #define little_endian(x) (endian_switch(x)) #define long_little_endian(x) (long_endian_switch(x)) #else #define big_endian(x) (endian_switch(x)) #define long_big_endian(x) (long_endian_switch(x)) #define little_endian(x) (x) #define long_little_endian(x) (x) #endif /* ___TNEF_BYTE_ORDER */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #endif /* _CONFIG_H */ p3scan-2.3.2/ripmime-1.4.0.6/tnef/mapidefs.h0000644000175000001440000002330210347164530016707 0ustar jlaiusers/* * M A P I D E F S . H * * Definitions used by MAPI clients and service providers. * * Copyright 1986-1996 Microsoft Corporation. All Rights Reserved. */ #ifndef MAPIDEFS_H #define MAPIDEFS_H /* Array dimension for structures with variable-sized arrays at the end. */ /* Simple data types */ typedef WORD WCHAR; #ifdef UNICODE typedef WCHAR TCHAR; #else typedef char TCHAR; #endif typedef WCHAR * LPWSTR; typedef const WCHAR * LPCWSTR; typedef TCHAR * LPTSTR; typedef const TCHAR * LPCTSTR; typedef BYTE * LPBYTE; typedef ULONG * LPULONG; #ifndef __LHANDLE #define __LHANDLE typedef unsigned long LHANDLE, * LPLHANDLE; #endif #if !defined(_WINBASE_) && !defined(_FILETIME_) #define _FILETIME_ typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, * LPFILETIME; #endif /* * This flag is used in many different MAPI calls to signify that * the object opened by the call should be modifiable (MAPI_MODIFY). * If the flag MAPI_MAX_ACCESS is set, the object returned should be * returned at the maximum access level allowed. An additional * property available on the object (PR_ACCESS_LEVEL) uses the same * MAPI_MODIFY flag to say just what this new access level is. */ #define MAPI_MODIFY ((ULONG) 0x00000001) /* * The following flags are used to indicate to the client what access * level is permissible in the object. They appear in PR_ACCESS in * message and folder objects as well as in contents and associated * contents tables */ #define MAPI_ACCESS_MODIFY ((ULONG) 0x00000001) #define MAPI_ACCESS_READ ((ULONG) 0x00000002) #define MAPI_ACCESS_DELETE ((ULONG) 0x00000004) #define MAPI_ACCESS_CREATE_HIERARCHY ((ULONG) 0x00000008) #define MAPI_ACCESS_CREATE_CONTENTS ((ULONG) 0x00000010) #define MAPI_ACCESS_CREATE_ASSOCIATED ((ULONG) 0x00000020) /* * The MAPI_UNICODE flag is used in many different MAPI calls to signify * that strings passed through the interface are in Unicode (a 16-bit * character set). The default is an 8-bit character set. * * The value fMapiUnicode can be used as the 'normal' value for * that bit, given the application's default character set. */ #define MAPI_UNICODE ((ULONG) 0x80000000) #ifdef UNICODE #define fMapiUnicode MAPI_UNICODE #else #define fMapiUnicode 0 #endif /* successful HRESULT */ #define hrSuccess 0 /* Recipient types */ #ifndef MAPI_ORIG /* also defined in mapi.h */ #define MAPI_ORIG 0 /* Recipient is message originator */ #define MAPI_TO 1 /* Recipient is a primary recipient */ #define MAPI_CC 2 /* Recipient is a copy recipient */ #define MAPI_BCC 3 /* Recipient is blind copy recipient */ #define MAPI_P1 0x10000000 /* Recipient is a P1 resend recipient */ #define MAPI_SUBMITTED 0x80000000 /* Recipient is already processed */ /* #define MAPI_AUTHORIZE 4 recipient is a CMC authorizing user */ /*#define MAPI_DISCRETE 0x10000000 Recipient is a P1 resend recipient */ #endif /* Bit definitions for abFlags[0] of ENTRYID */ #define MAPI_SHORTTERM 0x80 #define MAPI_NOTRECIP 0x40 #define MAPI_THISSESSION 0x20 #define MAPI_NOW 0x10 #define MAPI_NOTRESERVED 0x08 /* Bit definitions for abFlags[1] of ENTRYID */ #define MAPI_COMPOUND 0x80 /* ENTRYID */ typedef struct { BYTE abFlags[4]; BYTE ab[MAPI_DIM]; } ENTRYID, *LPENTRYID; #define CbNewENTRYID(_cb) (offsetof(ENTRYID,ab) + (_cb)) #define CbENTRYID(_cb) (offsetof(ENTRYID,ab) + (_cb)) /* Byte-order-independent version of GUID (world-unique identifier) */ typedef struct _MAPIUID { BYTE ab[16]; } MAPIUID, * LPMAPIUID; /* Note: need to include C run-times (memory.h) to use this macro */ #define IsEqualMAPIUID(lpuid1, lpuid2) (!memcmp(lpuid1, lpuid2, sizeof(MAPIUID))) /* * Constants for one-off entry ID: * The MAPIUID that identifies the one-off provider; * the flag that defines whether the embedded strings are Unicode; * the flag that specifies whether the recipient gets TNEF or not. */ #define MAPI_ONE_OFF_UID { 0x81, 0x2b, 0x1f, 0xa4, 0xbe, 0xa3, 0x10, 0x19, 0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02 } #define MAPI_ONE_OFF_UNICODE 0x8000 #define MAPI_ONE_OFF_NO_RICH_INFO 0x0001 /* Object type */ #define MAPI_STORE ((ULONG) 0x00000001) /* Message Store */ #define MAPI_ADDRBOOK ((ULONG) 0x00000002) /* Address Book */ #define MAPI_FOLDER ((ULONG) 0x00000003) /* Folder */ #define MAPI_ABCONT ((ULONG) 0x00000004) /* Address Book Container */ #define MAPI_MESSAGE ((ULONG) 0x00000005) /* Message */ #define MAPI_MAILUSER ((ULONG) 0x00000006) /* Individual Recipient */ #define MAPI_ATTACH ((ULONG) 0x00000007) /* Attachment */ #define MAPI_DISTLIST ((ULONG) 0x00000008) /* Distribution List Recipient */ #define MAPI_PROFSECT ((ULONG) 0x00000009) /* Profile Section */ #define MAPI_STATUS ((ULONG) 0x0000000A) /* Status Object */ #define MAPI_SESSION ((ULONG) 0x0000000B) /* Session */ #define MAPI_FORMINFO ((ULONG) 0x0000000C) /* Form Information */ /* * Maximum length of profile names and passwords, not including * the null termination character. */ #ifndef cchProfileNameMax #define cchProfileNameMax 64 #define cchProfilePassMax 64 #endif /* Property Types */ #define MV_FLAG 0x1000 /* Multi-value flag */ #define PT_UNSPECIFIED ((ULONG) 0) /* (Reserved for interface use) type doesn't matter to caller */ #define PT_NULL ((ULONG) 1) /* NULL property value */ #define PT_I2 ((ULONG) 2) /* Signed 16-bit value */ #define PT_LONG ((ULONG) 3) /* Signed 32-bit value */ #define PT_R4 ((ULONG) 4) /* 4-byte floating point */ #define PT_DOUBLE ((ULONG) 5) /* Floating point double */ #define PT_CURRENCY ((ULONG) 6) /* Signed 64-bit int (decimal w/ 4 digits right of decimal pt) */ #define PT_APPTIME ((ULONG) 7) /* Application time */ #define PT_ERROR ((ULONG) 10) /* 32-bit error value */ #define PT_BOOLEAN ((ULONG) 11) /* 16-bit boolean (non-zero true) */ #define PT_OBJECT ((ULONG) 13) /* Embedded object in a property */ #define PT_I8 ((ULONG) 20) /* 8-byte signed integer */ #define PT_STRING8 ((ULONG) 30) /* Null terminated 8-bit character string */ #define PT_UNICODE ((ULONG) 31) /* Null terminated Unicode string */ #define PT_SYSTIME ((ULONG) 64) /* FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */ #define PT_CLSID ((ULONG) 72) /* OLE GUID */ #define PT_BINARY ((ULONG) 258) /* Uninterpreted (counted byte array) */ /* Changes are likely to these numbers, and to their structures. */ /* Alternate property type names for ease of use */ #define PT_SHORT PT_I2 #define PT_I4 PT_LONG #define PT_FLOAT PT_R4 #define PT_R8 PT_DOUBLE #define PT_LONGLONG PT_I8 /* * The type of a MAPI-defined string property is indirected, so * that it defaults to Unicode string on a Unicode platform and to * String8 on an ANSI or DBCS platform. * * Macros are defined here both for the property type, and for the * field of the property value structure which should be * dereferenced to obtain the string pointer. */ #ifdef UNICODE #define PT_TSTRING PT_UNICODE #define PT_MV_TSTRING (MV_FLAG|PT_UNICODE) #define LPSZ lpszW #define LPPSZ lppszW #define MVSZ MVszW #else #define PT_TSTRING PT_STRING8 #define PT_MV_TSTRING (MV_FLAG|PT_STRING8) #define LPSZ lpszA #define LPPSZ lppszA #define MVSZ MVszA #endif /* Property Tags * * By convention, MAPI never uses 0 or FFFF as a property ID. * Use as null values, initializers, sentinels, or what have you. */ #define PROP_TYPE_MASK ((ULONG)0x0000FFFF) /* Mask for Property type */ #define PROP_TYPE(ulPropTag) (((ULONG)(ulPropTag))&PROP_TYPE_MASK) #define PROP_ID(ulPropTag) (((ULONG)(ulPropTag))>>16) #define PROP_TAG(ulPropType,ulPropID) ((((ULONG)(ulPropID))<<16)|((ULONG)(ulPropType))) #define PROP_ID_NULL 0 #define PROP_ID_INVALID 0xFFFF #define PR_NULL PROP_TAG( PT_NULL, PROP_ID_NULL) #if 0 #define CHANGE_PROP_TYPE(ulPropTag, ulPropType) \ (((ULONG)0xFFFF0000 & ulPropTag) | ulPropType) #endif /* Multi-valued Property Types */ #define PT_MV_I2 (MV_FLAG|PT_I2) #define PT_MV_LONG (MV_FLAG|PT_LONG) #define PT_MV_R4 (MV_FLAG|PT_R4) #define PT_MV_DOUBLE (MV_FLAG|PT_DOUBLE) #define PT_MV_CURRENCY (MV_FLAG|PT_CURRENCY) #define PT_MV_APPTIME (MV_FLAG|PT_APPTIME) #define PT_MV_SYSTIME (MV_FLAG|PT_SYSTIME) #define PT_MV_STRING8 (MV_FLAG|PT_STRING8) #define PT_MV_BINARY (MV_FLAG|PT_BINARY) #define PT_MV_UNICODE (MV_FLAG|PT_UNICODE) #define PT_MV_CLSID (MV_FLAG|PT_CLSID) #define PT_MV_I8 (MV_FLAG|PT_I8) /* Alternate property type names for ease of use */ #define PT_MV_SHORT PT_MV_I2 #define PT_MV_I4 PT_MV_LONG #define PT_MV_FLOAT PT_MV_R4 #define PT_MV_R8 PT_MV_DOUBLE #define PT_MV_LONGLONG PT_MV_I8 /* * Property type reserved bits * * MV_INSTANCE is used as a flag in table operations to request * that a multi-valued property be presented as a single-valued * property appearing in multiple rows. */ #define MV_INSTANCE 0x2000 #define MVI_FLAG (MV_FLAG | MV_INSTANCE) #define MVI_PROP(tag) ((tag) | MVI_FLAG) #endif /* MAPIDEFS_H */ p3scan-2.3.2/ripmime-1.4.0.6/tnef/tnef_api.h0000644000175000001440000000064510347164530016711 0ustar jlaiusers// API for external programs wanting to use TNEF decoding // #ifndef __TNEF_API__ #define __TNEF_API__ int TNEF_init( void ); int TNEF_main( char *filename ); int TNEF_set_filename_report_fn( int (*ptr_to_fn)(char *, char *)); int TNEF_set_verbosity( int level ); int TNEF_set_verbosity_contenttype( int level ); int TNEF_set_debug( int level ); int TNEF_set_path( char *path ); int TNEF_set_decode( int level ); #endif p3scan-2.3.2/ripmime-1.4.0.6/tnef/tnef.c0000644000175000001440000005007710347164530016057 0ustar jlaiusers/*************************************************************************** * tnef2txt * A program to decode application/ms-tnef MIME attachments into text * for those fortunate enough not to be running either a Microsoft * operating system or mailer. * * 18/10/2001 * Brutally cropped by Paul L Daniels (pldaniels@pldaniels.com) in order * to accommodate the needs of ripMIME/Xamime/Inflex without carrying too * much excess baggage. * * Brandon Long (blong@uiuc.edu), April 1997 * 1.0 Version * Supports most types, but doesn't decode properties. Maybe some other * time. * * 1.1 Version (7/1/97) * Supports saving of attAttachData to a file given by attAttachTitle * start of property decoding support * * 1.2 Version (7/19/97) * Some architectures don't like reading 16/32 bit data on unaligned * boundaries. Fixed, losing efficiency, but this doesn't really * need efficiency anyways. (Still...) * Also, the #pragma pack from the MSVC include file wasn't liked * by most Unix compilers, replaced with a GCCism. This should work * with GCC, but other compilers I don't know. * * 1.3 Version (7/22/97) * Ok, take out the DTR over the stream, now uses read_16. * * NOTE: THIS SOFTWARE IS FOR YOUR PERSONAL GRATIFICATION ONLY. I DON'T * IMPLY IN ANY LEGAL SENSE THAT THIS SOFTWARE DOES ANYTHING OR THAT IT WILL * BE USEFULL IN ANY WAY. But, you can send me fixes to it, I don't mind. ***************************************************************************/ #include #include #include #include #include #include #include "logger.h" #include "config.h" #include "tnef.h" #include "mapidefs.h" #include "mapitags.h" #include "tnef_api.h" #define VERSION "pldtnef/0.0.2" #ifndef FL #define FL __FILE__,__LINE__ #endif #define TNEF_VERBOSE ((TNEF_glb.verbose > 0)) #define TNEF_DEBUG ((TNEF_glb.debug > 0)) /** 20041207-1246:PLD: Added RT32 macro to allow for large numbers of read-tests **/ #define RT32( num_addr, offset ) if (read_32(num_addr, offset)==-1) return -1 #define TNEF_PATH_SIZE 1024 struct TNEF_globals { int verbose; int verbosity_contenttype; int debug; char path[ TNEF_PATH_SIZE +1]; int TNEF_Verbose; int savedata; uint8 *tnef_home; uint8 *tnef_limit; int (*filename_decoded_report)(char *, char *); // Pointer to our filename reporting function }; static struct TNEF_globals TNEF_glb; // The variables below have been pushed into the TNEF globals //int Verbose = FALSE; //int SaveData = FALSE; //uint8 *tnef_home; //uint8 *tnef_limit; /*-----------------------------------------------------------------\ Function Name : TNEF_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_init( void ) { TNEF_glb.verbose = 0; TNEF_glb.verbosity_contenttype = 0; TNEF_glb.debug = 0; TNEF_glb.savedata = 1; TNEF_glb.TNEF_Verbose = 0; TNEF_glb.filename_decoded_report = NULL; TNEF_glb.path[0] = '\0'; return 0; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_decode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_decode( int level ) { TNEF_glb.savedata = level; return TNEF_glb.savedata; } /*------------------------------------------------------------------------ Procedure: TNEF_set_path ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_path( char *path ) { snprintf( TNEF_glb.path, TNEF_PATH_SIZE , "%s", path); return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_set_verbosity ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_verbosity( int level ) { TNEF_glb.verbose = level; return TNEF_glb.verbose; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_verbosity_contenttype( int level ) { TNEF_glb.verbosity_contenttype = level; return TNEF_glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { TNEF_glb.filename_decoded_report = ptr_to_fn; return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_set_debug ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_debug( int level ) { TNEF_glb.debug = level; TNEF_set_verbosity( level ); return TNEF_glb.debug; } /* Some systems don't like to read unaligned data */ /*------------------------------------------------------------------------ Procedure: read_32 ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_32( uint32 *value, uint8 *tsp) { uint8 a,b,c,d; if ((tsp +4) > TNEF_glb.tnef_limit) { if ((TNEF_VERBOSE)||(TNEF_DEBUG)) LOGGER_log("%s:%d:TNEF_read_32:ERROR: Attempting to read beyond end of memory block",FL); return -1; } a = *tsp; b = *(tsp+1); c = *(tsp+2); d = *(tsp+3); *value = long_little_endian(a<<24 | b<<16 | c<<8 | d); return 0; } /*------------------------------------------------------------------------ Procedure: read_16 ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_16( uint16 *value, uint8 *tsp) { uint8 a,b; if ((tsp +2) > TNEF_glb.tnef_limit) { if ((TNEF_VERBOSE)||(TNEF_DEBUG)) LOGGER_log("%s:%d:TNEF_read_16:ERROR: Attempting to read past end\n",FL); return -1; } // if (TNEF_DEBUG) fprintf(stderr,"Read_16: Offset read %d\n", tsp -tnef_home); a = *tsp; b = *(tsp + 1); *value = little_endian(a<<8 | b); return 0; } /*------------------------------------------------------------------------ Procedure: make_string ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ char *make_string(uint8 *tsp, int size) { static char s[256] = ""; snprintf(s,sizeof(s),"%s",tsp); /** 20041106-0929:PLD: Remove this ugly old code and use snprintf() instead int len = (size>sizeof(s)-1) ? sizeof(s)-1 : size; strncpy(s,tsp, len); s[len] = '\0'; **/ return s; } /*------------------------------------------------------------------------ Procedure: save_attach_data ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int save_attach_data(char *title, uint8 *tsp, uint32 size) { FILE *out; char filename[1024]; snprintf(filename, sizeof(filename),"%s/%s", TNEF_glb.path, title ); out = fopen(filename, "w"); if (!out) { LOGGER_log("%s:%d:TNEF_save_attach_data:ERROR: Failed opening file %s for writing (%s)\n", FL, filename, strerror(errno)); return -1; } fwrite(tsp, sizeof(uint8), size, out); fclose(out); return 0; } /*------------------------------------------------------------------------ Procedure: handle_props ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int handle_props(uint8 *tsp) { int bytes = 0; uint32 num_props = 0; uint32 x = 0; RT32(&num_props, tsp); bytes += sizeof(num_props); while (x < num_props) { uint32 prop_tag; uint32 num; char filename[256]; static int file_num = 0; RT32(&prop_tag, tsp+bytes); bytes += sizeof(prop_tag); switch (prop_tag & PROP_TYPE_MASK) { case PT_BINARY: RT32(&num, tsp+bytes); bytes += sizeof(num); RT32(&num, tsp+bytes); bytes += sizeof(num); if (prop_tag == PR_RTF_COMPRESSED) { sprintf (filename, "XAM_%d.rtf", file_num); file_num++; save_attach_data(filename, tsp+bytes, num); } /* num + PAD */ bytes += num + ((num % 4) ? (4 - num%4) : 0); break; case PT_STRING8: RT32(&num, tsp+bytes); bytes += sizeof(num); RT32(&num, tsp+bytes); bytes += sizeof(num); make_string(tsp+bytes,num); bytes += num + ((num % 4) ? (4 - num%4) : 0); break; case PT_UNICODE: case PT_OBJECT: break; case PT_I2: bytes += 2; break; case PT_LONG: bytes += 4; break; case PT_R4: bytes += 4; break; case PT_DOUBLE: bytes += 8; break; case PT_CURRENCY: case PT_APPTIME: case PT_ERROR: bytes += 4; break; case PT_BOOLEAN: bytes += 4; break; case PT_I8: bytes += 8; case PT_SYSTIME: bytes += 8; break; } x++; } return 0; } /*------------------------------------------------------------------------ Procedure: default_handler ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int default_handler(uint32 attribute, uint8 *tsp, uint32 size) { uint16 type = ATT_TYPE(attribute); switch (type) { case atpTriples: break; case atpString: case atpText: break; case atpDate: break; case atpShort: break; case atpLong: break; case atpByte: break; case atpWord: break; case atpDword: break; default: break; } return 0; } /*------------------------------------------------------------------------ Procedure: read_attribute ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_attribute(uint8 *tsp) { int bytes = 0, header = 0; int rv = 0; uint32 attribute; uint8 component = 0; uint32 size = 0; uint16 checksum = 0; static char attach_title[256] = { 0 }; static uint32 attach_size = 0; //static uint32 attach_loc = 0; // 2003-02-22-1231-PLD static uint8 *attach_loc = 0; component = *tsp; bytes += sizeof(uint8); // Read the attributes of this component if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Reading Attribute...\n",FL); rv = read_32(&attribute, tsp+bytes); if (rv == -1) return -1; bytes += sizeof(attribute); // Read the size of the information we have to read if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute: Reading Size...\n",FL); rv = read_32(&size, tsp+bytes); if (rv == -1) return -1; bytes += sizeof(size); // The header size equals the sum of all the things we've read // so far. header = bytes; // The is a bit of a tricky one [if you're being slow // it moves the number of bytes ahead by the amount of data of // the attribute we're about to read, so that for next // "read_attribute()" // call starts in the right place. bytes += size; // Read in the checksum for this component // // AMMENDMENT - 19/07/02 - 17H01 // Small code change to deal with strange sitations that occur with non // english characters. - Submitted by wtcheuk@netvigator.com @ 19/07/02 if ( bytes < 0 ) return -1; // --END of ammendment. if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Reading Checksum...(offset %d, bytes=%d)\n", FL, tsp -TNEF_glb.tnef_home, bytes); read_16(&checksum, tsp+bytes); bytes += sizeof(checksum); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Decoding attribute %d\n", FL, attribute); switch (attribute) { case attNull: default_handler(attribute, tsp+header, size); break; case attFrom: default_handler(attribute, tsp+header, size); break; case attSubject: break; case attDateSent: break; case attDateRecd: break; case attMessageStatus: break; case attMessageClass: break; case attMessageID: break; case attParentID: break; case attConversationID: break; case attBody: default_handler(attribute, tsp+header, size); break; case attPriority: break; case attAttachData: attach_size=size; // attach_loc =(int)tsp+header; // 2003-02-22-1232-PLD attach_loc =(uint8 *)tsp+header; if (TNEF_glb.savedata && strlen(attach_title)>0 && attach_size > 0) { if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size)) { if (TNEF_VERBOSE) { if (TNEF_glb.filename_decoded_report == NULL) { LOGGER_log("Decoding: %s\n", attach_title); } else { TNEF_glb.filename_decoded_report( attach_title, (TNEF_glb.verbosity_contenttype>0?"tnef":NULL)); } } } else { LOGGER_log("%s:%d:TNEF_read_attribute:ERROR: While saving attachment '%s'\n", FL, attach_title); } } break; case attAttachTitle: strncpy(attach_title, make_string(tsp+header,size),255); if (TNEF_glb.savedata && strlen(attach_title)>0 && attach_size > 0) { if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size)) { if (TNEF_VERBOSE) { if (TNEF_glb.filename_decoded_report == NULL) { LOGGER_log("Decoding: %s\n", attach_title); } else { TNEF_glb.filename_decoded_report( attach_title, (TNEF_glb.verbosity_contenttype>0?"tnef":NULL)); } } } else { LOGGER_log("%s:%d:TNEF_read_attribute:ERROR: While saving attachment '%s'\n", FL, attach_title); } } break; case attAttachMetaFile: default_handler(attribute, tsp+header, size); break; case attAttachCreateDate: break; case attAttachModifyDate: break; case attDateModified: break; case attAttachTransportFilename: default_handler(attribute, tsp+header, size); break; case attAttachRenddata: attach_title[0]=0; attach_size=0; attach_loc=0; default_handler(attribute, tsp+header, size); break; case attMAPIProps: if (handle_props(tsp+header)==-1) return -1; break; case attRecipTable: default_handler(attribute, tsp+header, size); break; case attAttachment: default_handler(attribute, tsp+header, size); break; case attTnefVersion: { uint32 version; rv = read_32(&version, tsp+header); if (rv == -1) return -1; } break; case attOemCodepage: default_handler(attribute, tsp+header, size); break; case attOriginalMessageClass: break; case attOwner: default_handler(attribute, tsp+header, size); break; case attSentFor: default_handler(attribute, tsp+header, size); break; case attDelegate: default_handler(attribute, tsp+header, size); break; case attDateStart: break; case attDateEnd: break; case attAidOwner: default_handler(attribute, tsp+header, size); break; case attRequestRes: default_handler(attribute, tsp+header, size); break; default: default_handler(attribute, tsp+header, size); break; } return bytes; } /*------------------------------------------------------------------------ Procedure: decode_tnef ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_decode_tnef(uint8 *tnef_stream, int size) { int ra_response; uint32 tnefs; uint16 tnef_attachkey; uint8 *tsp; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Start. Size = %d\n", FL,size); // TSP == TNEF Stream Pointer (well memory block actually!) // tsp = tnef_stream; // Read in the signature of this TNEF // ra_response = read_32(&tnefs, tsp); if ((ra_response != -1)&&(TNEF_SIGNATURE == tnefs)) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF signature is good\n",FL); } else { if (TNEF_VERBOSE) LOGGER_log("%s:%d:TNEF_decode_tnef:WARNING: Bad TNEF signature, expecting %lx got %lx\n",FL,TNEF_SIGNATURE,tnefs); } // Move tsp pointer along // tsp += sizeof(TNEF_SIGNATURE); /** Read the TNEF Attach key **/ read_16(&tnef_attachkey, tsp); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF Attach Key: %x\n",FL,tnef_attachkey); // Move tsp pointer along // tsp += sizeof(uint16); // While we still have more bytes to process, // go through entire memory block and extract // all the required attributes and files // if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF - Commence reading attributes\n",FL); while ((tsp - tnef_stream) < size) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Offset = %d\n", FL,tsp -TNEF_glb.tnef_home); ra_response = read_attribute(tsp); if ( ra_response > 0 ) { tsp += ra_response; } else { // Must find out /WHY/ this happens, and, how to rectify the issue. tsp++; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:WARNING: TNEF - Attempting to read attribute at %d resulted in a sub-zero response, ending decoding to be safe\n",FL,tsp); break; } } if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Done.\n",FL); return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_main ID:1 Purpose: Decodes a given TNEF encoded file Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_main( char *filename ) { FILE *fp; struct stat sb; uint8 *tnef_stream; int size, nread; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: Start, decoding %s\n",FL, filename); if (TNEF_glb.savedata == 0 ) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_name:DEBUG: decode_tnef is set to 0, not decoding file.",FL); return 0; } // Test to see if the file actually exists // if (stat(filename,&sb) == -1) { LOGGER_log("%s:%d:TNEF_main:ERROR: while attempting to get details on file %s (%s)\n", FL, filename,strerror(errno)); return -1; } // Get the filesize // size = sb.st_size; // Allocate enough memory to read in the ENTIRE file // FIXME - This could be a real consumer if multiple // instances of TNEF decoding is going on // TNEF_glb.tnef_home = tnef_stream = (uint8 *)malloc(size); TNEF_glb.tnef_limit = TNEF_glb.tnef_home +size; // If we were unable to allocate enough memory, then we // should report this // if (tnef_stream == NULL) { LOGGER_log("%s:%d:TNEF_main:ERROR: When allocating %d bytes for loading file (%s)\n", FL, size,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Attempt to open up the TNEF encoded file... if it fails // then report the failed condition to syslog // if ((fp = fopen(filename,"r")) == NULL) { LOGGER_log("%s:%d:TNEF_main:ERROR: opening file %s for reading (%s)\n", FL, filename,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Attempt to read in the entire file // nread = fread(tnef_stream, sizeof(uint8), size, fp); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: Read %d bytes\n", FL, nread); // If we did not read in all the bytes, then let syslogs know! // if (nread < size) { LOGGER_log("%s:%d:TNEF_main:ERROR: while reading stream from file %s (%s)\n", FL, filename,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Close the file // fclose(fp); // Proceed to decode the file // TNEF_decode_tnef(tnef_stream,size); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: finished decoding.\n",FL); return 0; } //--------------------------END. p3scan-2.3.2/ripmime-1.4.0.6/tnef/tnef.h0000644000175000001440000003353110347164530016060 0ustar jlaiusers/* * Taken from the Win32 SDK or the MSVC4 include files, I'm not sure which. * The document describing the TNEF format alludes to this document for more * information. This file was stripped a bit to allow it to compile with * GCC and without random other Windows header files so it could be used * to decode TNEF bitstreams with tnef2txt. * * T N E F . H * * * This file contains structure and function definitions for the * MAPI implementation of the Transport Neutral Encapsilation Format * used by MAPI providers for the neutral serialization of a MAPI * message. This implementation sits on top of the IStream object as * documented in the OLE 2 Specs. * * Copyright 1986-1996 Microsoft Corporation. All Rights Reserved. */ #ifndef TNEF_H #define TNEF_H #ifdef __cplusplus extern "C" { #endif #ifndef BEGIN_INTERFACE #define BEGIN_INTERFACE #endif #ifndef MAPI_DIM #define MAPI_DIM 1 #endif #define TNTNoffsetof(s,m) (unsigned long)&(((s *)0)->m) /* ------------------------------------ */ /* TNEF Problem and TNEF Problem Arrays */ /* ------------------------------------ */ typedef struct _STnefProblem { ULONG ulComponent; ULONG ulAttribute; ULONG ulPropTag; SCODE scode; } STnefProblem; typedef struct _STnefProblemArray { ULONG cProblem; STnefProblem aProblem[MAPI_DIM]; } STnefProblemArray, FAR * LPSTnefProblemArray; #if 0 #define CbNewSTnefProblemArray(_cprob) \ (TNoffsetof(STnefProblemArray,aProblem) + (_cprob)*sizeof(STnefProblem)) #define CbSTnefProblemArray(_lparray) \ (TNoffsetof(STnefProblemArray,aProblem) + \ (UINT) ((_lparray)->cProblem*sizeof(STnefProblem))) #endif /* Pointers to TNEF Interface ---------------------------------------- */ #if 0 DECLARE_MAPI_INTERFACE_PTR(ITnef, LPITNEF); #endif /* OpenTNEFStream */ #define TNEF_DECODE ((ULONG) 0) #define TNEF_ENCODE ((ULONG) 2) #define TNEF_PURE ((ULONG) 0x00010000) #define TNEF_COMPATIBILITY ((ULONG) 0x00020000) #define TNEF_BEST_DATA ((ULONG) 0x00040000) #define TNEF_COMPONENT_ENCODING ((ULONG) 0x80000000) /* AddProps, ExtractProps */ #define TNEF_PROP_INCLUDE ((ULONG) 0x00000001) #define TNEF_PROP_EXCLUDE ((ULONG) 0x00000002) #define TNEF_PROP_CONTAINED ((ULONG) 0x00000004) #define TNEF_PROP_MESSAGE_ONLY ((ULONG) 0x00000008) #define TNEF_PROP_ATTACHMENTS_ONLY ((ULONG) 0x00000010) #define TNEF_PROP_CONTAINED_TNEF ((ULONG) 0x00000040) /* FinishComponent */ #define TNEF_COMPONENT_MESSAGE ((ULONG) 0x00001000) #define TNEF_COMPONENT_ATTACHMENT ((ULONG) 0x00002000) #if 0 #define MAPI_ITNEF_METHODS(IPURE) \ MAPIMETHOD(AddProps) \ (THIS_ ULONG ulFlags, \ ULONG ulElemID, \ LPVOID lpvData, \ LPSPropTagArray lpPropList) IPURE; \ MAPIMETHOD(ExtractProps) \ (THIS_ ULONG ulFlags, \ LPSPropTagArray lpPropList, \ LPSTnefProblemArray FAR * lpProblems) IPURE; \ MAPIMETHOD(Finish) \ (THIS_ ULONG ulFlags, \ WORD FAR * lpKey, \ LPSTnefProblemArray FAR * lpProblems) IPURE; \ MAPIMETHOD(OpenTaggedBody) \ (THIS_ LPMESSAGE lpMessage, \ ULONG ulFlags, \ LPSTREAM FAR * lppStream) IPURE; \ MAPIMETHOD(SetProps) \ (THIS_ ULONG ulFlags, \ ULONG ulElemID, \ ULONG cValues, \ LPSPropValue lpProps) IPURE; \ MAPIMETHOD(EncodeRecips) \ (THIS_ ULONG ulFlags, \ LPMAPITABLE lpRecipientTable) IPURE; \ MAPIMETHOD(FinishComponent) \ (THIS_ ULONG ulFlags, \ ULONG ulComponentID, \ LPSPropTagArray lpCustomPropList, \ LPSPropValue lpCustomProps, \ LPSPropTagArray lpPropList, \ LPSTnefProblemArray FAR * lpProblems) IPURE; \ #undef INTERFACE #define INTERFACE ITnef DECLARE_MAPI_INTERFACE_(ITnef, IUnknown) { BEGIN_INTERFACE MAPI_IUNKNOWN_METHODS(PURE) MAPI_ITNEF_METHODS(PURE) }; STDMETHODIMP OpenTnefStream( LPVOID lpvSupport, LPSTREAM lpStream, LPTSTR lpszStreamName, ULONG ulFlags, LPMESSAGE lpMessage, WORD wKeyVal, LPITNEF FAR * lppTNEF); typedef HRESULT (STDMETHODCALLTYPE FAR * LPOPENTNEFSTREAM) ( LPVOID lpvSupport, LPSTREAM lpStream, LPTSTR lpszStreamName, ULONG ulFlags, LPMESSAGE lpMessage, WORD wKeyVal, LPITNEF FAR * lppTNEF); STDMETHODIMP OpenTnefStreamEx( LPVOID lpvSupport, LPSTREAM lpStream, LPTSTR lpszStreamName, ULONG ulFlags, LPMESSAGE lpMessage, WORD wKeyVal, LPADRBOOK lpAdressBook, LPITNEF FAR * lppTNEF); typedef HRESULT (STDMETHODCALLTYPE FAR * LPOPENTNEFSTREAMEX) ( LPVOID lpvSupport, LPSTREAM lpStream, LPTSTR lpszStreamName, ULONG ulFlags, LPMESSAGE lpMessage, WORD wKeyVal, LPADRBOOK lpAdressBook, LPITNEF FAR * lppTNEF); STDMETHODIMP GetTnefStreamCodepage ( LPSTREAM lpStream, ULONG FAR * lpulCodepage, ULONG FAR * lpulSubCodepage); typedef HRESULT (STDMETHODCALLTYPE FAR * LPGETTNEFSTREAMCODEPAGE) ( LPSTREAM lpStream, ULONG FAR * lpulCodepage, ULONG FAR * lpulSubCodepage); #define OPENTNEFSTREAM "OpenTnefStream" #define OPENTNEFSTREAMEX "OpenTnefStreamEx" #define GETTNEFSTREAMCODEPAGE "GetTnefStreamCodePage" #endif /* -------------------------- */ /* TNEF Signature and Version */ /* -------------------------- */ #define MAKE_TNEF_VERSION(_mj,_mn) (((ULONG)(0x0000FFFF & _mj) << 16) | (ULONG)(0x0000FFFF & _mn)) #define TNEF_SIGNATURE ((ULONG) 0x223E9F78) #define TNEF_VERSION ((ULONG) MAKE_TNEF_VERSION(1,0)) /* ------------------------------------------- */ /* TNEF Down-level Attachment Types/Structures */ /* ------------------------------------------- */ typedef WORD ATYP; enum { atypNull, atypFile, atypOle, atypPicture, atypMax }; #define MAC_BINARY ((DWORD) 0x00000001) typedef struct _renddata { ATYP atyp; ULONG ulPosition; WORD dxWidth; WORD dyHeight; DWORD dwFlags; } RENDDATA, *PRENDDATA; /* ----------------------------------- */ /* TNEF Down-level Date/Time Structure */ /* ----------------------------------- */ typedef struct _dtr { WORD wYear; WORD wMonth; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wDayOfWeek; } DTR; /* ----------------------------- */ /* TNEF Down-level Message Flags */ /* ----------------------------- */ #define fmsNull ((BYTE) 0x00) #define fmsModified ((BYTE) 0x01) #define fmsLocal ((BYTE) 0x02) #define fmsSubmitted ((BYTE) 0x04) #define fmsRead ((BYTE) 0x20) #define fmsHasAttach ((BYTE) 0x80) /* ----------------------------------------- */ /* TNEF Down-level Triple Address Structures */ /* ----------------------------------------- */ #define trpidNull ((WORD) 0x0000) #define trpidUnresolved ((WORD) 0x0001) #define trpidResolvedNSID ((WORD) 0x0002) #define trpidResolvedAddress ((WORD) 0x0003) #define trpidOneOff ((WORD) 0x0004) #define trpidGroupNSID ((WORD) 0x0005) #define trpidOffline ((WORD) 0x0006) #define trpidIgnore ((WORD) 0x0007) #define trpidClassEntry ((WORD) 0x0008) #define trpidResolvedGroupAddress ((WORD) 0x0009) typedef struct _trp { WORD trpid; WORD cbgrtrp; WORD cch; WORD cbRgb; } TRP, *PTRP, *PGRTRP, FAR * LPTRP; #define CbOfTrp(_p) (sizeof(TRP) + (_p)->cch + (_p)->cbRgb) #define LpszOfTrp(_p) ((LPSTR)(((LPTRP) (_p)) + 1)) #define LpbOfTrp(_p) (((LPBYTE)(((LPTRP)(_p)) + 1)) + (_p)->cch) #define LptrpNext(_p) ((LPTRP)((LPBYTE)(_p) + CbOfTrp(_p))) typedef DWORD XTYPE; #define xtypeUnknown ((XTYPE) 0) #define xtypeInternet ((XTYPE) 6) #define cbDisplayName 41 #define cbEmailName 11 #define cbSeverName 12 typedef struct _ADDR_ALIAS { char rgchName[cbDisplayName]; char rgchEName[cbEmailName]; char rgchSrvr[cbSeverName]; ULONG dibDetail; WORD type; } ADDRALIAS, FAR * LPADDRALIAS; #define cbALIAS sizeof(ALIAS) #define cbTYPE 16 #define cbMaxIdData 200 typedef struct _NSID { DWORD dwSize; unsigned char uchType[cbTYPE]; XTYPE xtype; LONG lTime; union { ADDRALIAS alias; char rgchInterNet[1]; } address; } NSID, * LPNSID; #define cbNSID sizeof(NSID) /* -------------------------- */ /* TNEF Down-level Priorities */ /* -------------------------- */ #define prioLow 3 #define prioNorm 2 #define prioHigh 1 /* ------------------------------------- */ /* TNEF Down-level Attributes/Properties */ /* ------------------------------------- */ #define atpTriples ((WORD) 0x0000) #define atpString ((WORD) 0x0001) #define atpText ((WORD) 0x0002) #define atpDate ((WORD) 0x0003) #define atpShort ((WORD) 0x0004) #define atpLong ((WORD) 0x0005) #define atpByte ((WORD) 0x0006) #define atpWord ((WORD) 0x0007) #define atpDword ((WORD) 0x0008) #define atpMax ((WORD) 0x0009) #define LVL_MESSAGE ((BYTE) 0x01) #define LVL_ATTACHMENT ((BYTE) 0x02) #define ATT_ID(_att) ((WORD) ((_att) & 0x0000FFFF)) #define ATT_TYPE(_att) ((WORD) (((_att) >> 16) & 0x0000FFFF)) #define ATT(_atp, _id) ((((DWORD) (_atp)) << 16) | ((WORD) (_id))) #define attNull ATT( 0, 0x0000) #define attFrom ATT( atpTriples, 0x8000) /* PR_ORIGINATOR_RETURN_ADDRESS */ #define attSubject ATT( atpString, 0x8004) /* PR_SUBJECT */ #define attDateSent ATT( atpDate, 0x8005) /* PR_CLIENT_SUBMIT_TIME */ #define attDateRecd ATT( atpDate, 0x8006) /* PR_MESSAGE_DELIVERY_TIME */ #define attMessageStatus ATT( atpByte, 0x8007) /* PR_MESSAGE_FLAGS */ #define attMessageClass ATT( atpWord, 0x8008) /* PR_MESSAGE_CLASS */ #define attMessageID ATT( atpString, 0x8009) /* PR_MESSAGE_ID */ #define attParentID ATT( atpString, 0x800A) /* PR_PARENT_ID */ #define attConversationID ATT( atpString, 0x800B) /* PR_CONVERSATION_ID */ #define attBody ATT( atpText, 0x800C) /* PR_BODY */ #define attPriority ATT( atpShort, 0x800D) /* PR_IMPORTANCE */ #define attAttachData ATT( atpByte, 0x800F) /* PR_ATTACH_DATA_xxx */ #define attAttachTitle ATT( atpString, 0x8010) /* PR_ATTACH_FILENAME */ #define attAttachMetaFile ATT( atpByte, 0x8011) /* PR_ATTACH_RENDERING */ #define attAttachCreateDate ATT( atpDate, 0x8012) /* PR_CREATION_TIME */ #define attAttachModifyDate ATT( atpDate, 0x8013) /* PR_LAST_MODIFICATION_TIME */ #define attDateModified ATT( atpDate, 0x8020) /* PR_LAST_MODIFICATION_TIME */ #define attAttachTransportFilename ATT( atpByte, 0x9001) /* PR_ATTACH_TRANSPORT_NAME */ #define attAttachRenddata ATT( atpByte, 0x9002) #define attMAPIProps ATT( atpByte, 0x9003) #define attRecipTable ATT( atpByte, 0x9004) /* PR_MESSAGE_RECIPIENTS */ #define attAttachment ATT( atpByte, 0x9005) #define attTnefVersion ATT( atpDword, 0x9006) #define attOemCodepage ATT( atpByte, 0x9007) #define attOriginalMessageClass ATT( atpWord, 0x0006) /* PR_ORIG_MESSAGE_CLASS */ #define attOwner ATT( atpByte, 0x0000) /* PR_RCVD_REPRESENTING_xxx or PR_SENT_REPRESENTING_xxx */ #define attSentFor ATT( atpByte, 0x0001) /* PR_SENT_REPRESENTING_xxx */ #define attDelegate ATT( atpByte, 0x0002) /* PR_RCVD_REPRESENTING_xxx */ #define attDateStart ATT( atpDate, 0x0006) /* PR_DATE_START */ #define attDateEnd ATT( atpDate, 0x0007) /* PR_DATE_END */ #define attAidOwner ATT( atpLong, 0x0008) /* PR_OWNER_APPT_ID */ #define attRequestRes ATT( atpShort, 0x0009) /* PR_RESPONSE_REQUESTED */ #ifdef __cplusplus } #endif #endif /* defined TNEF_H */ p3scan-2.3.2/ripmime-1.4.0.6/tnef/logger.c0000644000175000001440000001705310347164530016377 0ustar jlaiusers // Abstract logging system used to facilitate multiple modes // of logging #include #include #include #ifndef WIN32 #include #endif #include #include #include #include "logger.h" #ifndef WIN32 static int _LOGGER_mode = _LOGGER_SYSLOG; static int _LOGGER_syslog_mode = LOG_MAIL|LOG_INFO; #else static int _LOGGER_mode = _LOGGER_STDERR; static int _LOGGER_syslog_mode = 0; #endif static FILE *_LOGGER_outf; struct LOGGER_globals { int wrap; int wraplength; }; // Create and Initialise the global structure for LOGGER, // we init it to have NO wrapping. static struct LOGGER_globals LOGGER_glb={ 0, 0 }; /*------------------------------------------------------------------------ Procedure: LOGGER_get_file ID:1 Purpose: Returns the pointer to the file being used to output logs to Input: Output: Errors: ------------------------------------------------------------------------*/ FILE *LOGGER_get_file( void ) { return _LOGGER_outf; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_mode ID:1 Purpose: Sets the message/log output method, ie, stderr, stdout or syslog Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_mode( int modechoice ) { _LOGGER_mode = modechoice; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_file ID:1 Purpose: Sets the output file for when _LOGGER_mode is set to _LOGGER_file Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_file( FILE *f ) { _LOGGER_outf = f; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_syslog_mode ID:1 Purpose: Sets the mode that messaging to the syslog daemon will be sent as (ie, LOG_MAIL|LOG_INFO) Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_syslog_mode( int syslogmode ) { _LOGGER_syslog_mode = syslogmode; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_logfile ID:1 Purpose: Opens and setups the internal Log file file pointer with the log file as given by lfname Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_logfile( char *lfname ) { int result = 0; _LOGGER_outf = fopen(lfname,"a"); if (!_LOGGER_outf) { #ifndef WIN32 syslog(1,"LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)",lfname,strerror(errno)); #else fprintf(stderr, "LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)\n", lfname, strerror(errno)); #endif result = -1; } return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wraplength ID:1 Purpose: Sets the character count at which LOGGER will break a line Input: int length: Positive integer indicating number of chracters at which to wrap at Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wraplength( int length ) { if ( length >= 0 ) { LOGGER_glb.wraplength = length; } return LOGGER_glb.wraplength; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wrap ID:1 Purpose: Set log output wrapping to on or off Input: int level: 0 = no wrap, > 0 = wrap. Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wrap( int level ) { if ( level >= 0 ) { LOGGER_glb.wrap = level; } return LOGGER_glb.wrap; } /*------------------------------------------------------------------------ Procedure: LOGGER_close_logfile ID:1 Purpose: Closes the modules log file pointer. Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_close_logfile( void ) { int result = 0; if (_LOGGER_outf) fclose(_LOGGER_outf); return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_clean_output ID:1 Purpose: Checks through the output string for any characters which could cause potential 'isssues' with the data writing calls, items such as stray non-escaped % characters can cause havoc. Input: char *string: Raw string int maxsize: Maximum available buffer size for this string to expand to Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_clean_output( char *string, char **buffer ) { char *newstr; char *p, *q; char *next_space; int pc; int slen = strlen( string ); int line_size; int maxsize = slen *2; // First up, allocate maxsize bytes for a temporary new string. newstr = malloc(slen *2 +1); if ( newstr == NULL ) { // FIXME - Report an error here ... to -somewhere- return -1; } p = newstr; q = string; pc = 0; line_size = 0; while (slen--) { // Do we need to apply any wrapping to the output? If so then we // shall embark on a journey of strange space and distance // evaluations to determine if we should wrap now or later if ( LOGGER_glb.wrap > 0 ) { if (isspace((int)*q)) { next_space = strpbrk( (q+1), "\t\r\n\v " ); if (next_space != NULL) { if ((line_size +(next_space -q)) >= LOGGER_glb.wraplength) { *p = '\n'; p++; pc++; line_size = 0; } } } if ( line_size >= LOGGER_glb.wraplength ) { *p = '\n'; p++; pc++; line_size = 0; } } // If the string has a % in it, then we need to encode it as // a DOUBLE % symbol. if (*q == '%') { // if (strchr("fdlsxXn",*(q+1))) // { *p = '%'; p++; pc++; // } } // Copy the character of the string in *p = *q; // Move everything along. q++; p++; pc++; line_size++; if ( pc > (maxsize -1) ) { break; } } *p = '\0'; // This will have to be deallocated later! if (newstr) *buffer = newstr; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_log ID:1 Purpose: Logs the params as supplied to the required output as defined by LOGGER_set_output Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_log( char *format, ...) { va_list ptr; char tmpoutput[10240]; char linebreak[]="\n"; char nolinebreak[]=""; char *lineend; char *output; // get our variable arguments va_start(ptr,format); // produce output, and spit to the log file #ifdef NO_SNPRINTF vsprintf(tmpoutput, format, ptr); #else vsnprintf(tmpoutput,10240,format,ptr); #endif LOGGER_clean_output( tmpoutput, &output ); if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } // Send the output to the appropriate output destination switch (_LOGGER_mode) { case _LOGGER_STDERR: fprintf(stderr,"%s%s",output, lineend ); break; case _LOGGER_SYSLOG: syslog(_LOGGER_syslog_mode,output); break; case _LOGGER_STDOUT: fprintf(stdout,"%s%s",output, lineend); fflush(stdout); break; case _LOGGER_FILE: fprintf(_LOGGER_outf,"%s%s",output,lineend); fflush(_LOGGER_outf); break; default: fprintf(stdout,"LOGGER-Default: %s%s",output,lineend); } if (output) free(output); return 0; } p3scan-2.3.2/ripmime-1.4.0.6/tnef/logger.h0000644000175000001440000000110510347164530016373 0ustar jlaiusers #ifndef __LOGGER__ #define __LOGGER__ // LOGGER.h // #define _LOGGER_STDERR 1 #define _LOGGER_STDOUT 2 #define _LOGGER_FILE 3 #ifndef WIN32 #define _LOGGER_SYSLOG 4 #endif #ifndef FL #define FL __FILE__,__LINE__ #endif int LOGGER_log( char *format, ...); int LOGGER_set_output_mode( int modechoice ); int LOGGER_set_output_file( FILE *f ); int LOGGER_set_syslog_mode( int syslogmode ); int LOGGER_set_logfile( char *lfname ); int LOGGER_set_wraplength( int length ); int LOGGER_set_wrap( int level ); int LOGGER_close_logfile( void ); FILE *LOGGER_get_file( void ); #endif p3scan-2.3.2/ripmime-1.4.0.6/tnef/mapitags.h0000644000175000001440000020352010347164530016726 0ustar jlaiusers/* * M A P I T A G S . H * * Property tag definitions for standard properties of MAPI * objects. * * The following ranges should be used for all property IDs. Note that * property IDs for objects other than messages and recipients should * all fall in the range 0x3000 to 0x3FFF: * * From To Kind of property * -------------------------------- * 0001 0BFF MAPI_defined envelope property * 0C00 0DFF MAPI_defined per-recipient property * 0E00 0FFF MAPI_defined non-transmittable property * 1000 2FFF MAPI_defined message content property * * 3000 3FFF MAPI_defined property (usually not message or recipient) * * 4000 57FF Transport-defined envelope property * 5800 5FFF Transport-defined per-recipient property * 6000 65FF User-defined non-transmittable property * 6600 67FF Provider-defined internal non-transmittable property * 6800 7BFF Message class-defined content property * 7C00 7FFF Message class-defined non-transmittable * property * * 8000 FFFE User-defined Name-to-id mapped property * * The 3000-3FFF range is further subdivided as follows: * * From To Kind of property * -------------------------------- * 3000 33FF Common property such as display name, entry ID * 3400 35FF Message store object * 3600 36FF Folder or AB container * 3700 38FF Attachment * 3900 39FF Address book object * 3A00 3BFF Mail user * 3C00 3CFF Distribution list * 3D00 3DFF Profile section * 3E00 3FFF Status object * * Copyright 1986-1996 Microsoft Corporation. All Rights Reserved. */ #ifndef MAPITAGS_H #define MAPITAGS_H /* Determine if a property is transmittable. */ #define FIsTransmittable(ulPropTag) \ ((PROP_ID (ulPropTag) < (ULONG)0x0E00) || \ (PROP_ID (ulPropTag) >= (ULONG)0x8000) || \ ((PROP_ID (ulPropTag) >= (ULONG)0x1000) && (PROP_ID (ulPropTag) < (ULONG)0x6000)) || \ ((PROP_ID (ulPropTag) >= (ULONG)0x6800) && (PROP_ID (ulPropTag) < (ULONG)0x7C00))) /* * Message envelope properties */ #define PR_ACKNOWLEDGEMENT_MODE PROP_TAG( PT_LONG, 0x0001) #define PR_ALTERNATE_RECIPIENT_ALLOWED PROP_TAG( PT_BOOLEAN, 0x0002) #define PR_AUTHORIZING_USERS PROP_TAG( PT_BINARY, 0x0003) #define PR_AUTO_FORWARD_COMMENT PROP_TAG( PT_TSTRING, 0x0004) #define PR_AUTO_FORWARD_COMMENT_W PROP_TAG( PT_UNICODE, 0x0004) #define PR_AUTO_FORWARD_COMMENT_A PROP_TAG( PT_STRING8, 0x0004) #define PR_AUTO_FORWARDED PROP_TAG( PT_BOOLEAN, 0x0005) #define PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID PROP_TAG( PT_BINARY, 0x0006) #define PR_CONTENT_CORRELATOR PROP_TAG( PT_BINARY, 0x0007) #define PR_CONTENT_IDENTIFIER PROP_TAG( PT_TSTRING, 0x0008) #define PR_CONTENT_IDENTIFIER_W PROP_TAG( PT_UNICODE, 0x0008) #define PR_CONTENT_IDENTIFIER_A PROP_TAG( PT_STRING8, 0x0008) #define PR_CONTENT_LENGTH PROP_TAG( PT_LONG, 0x0009) #define PR_CONTENT_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x000A) #define PR_CONVERSATION_KEY PROP_TAG( PT_BINARY, 0x000B) #define PR_CONVERSION_EITS PROP_TAG( PT_BINARY, 0x000C) #define PR_CONVERSION_WITH_LOSS_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x000D) #define PR_CONVERTED_EITS PROP_TAG( PT_BINARY, 0x000E) #define PR_DEFERRED_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x000F) #define PR_DELIVER_TIME PROP_TAG( PT_SYSTIME, 0x0010) #define PR_DISCARD_REASON PROP_TAG( PT_LONG, 0x0011) #define PR_DISCLOSURE_OF_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x0012) #define PR_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x0013) #define PR_DL_EXPANSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0014) #define PR_EXPIRY_TIME PROP_TAG( PT_SYSTIME, 0x0015) #define PR_IMPLICIT_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0016) #define PR_IMPORTANCE PROP_TAG( PT_LONG, 0x0017) #define PR_IPM_ID PROP_TAG( PT_BINARY, 0x0018) #define PR_LATEST_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0019) #define PR_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x001A) #define PR_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x001A) #define PR_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x001A) #define PR_MESSAGE_DELIVERY_ID PROP_TAG( PT_BINARY, 0x001B) #define PR_MESSAGE_SECURITY_LABEL PROP_TAG( PT_BINARY, 0x001E) #define PR_OBSOLETED_IPMS PROP_TAG( PT_BINARY, 0x001F) #define PR_ORIGINALLY_INTENDED_RECIPIENT_NAME PROP_TAG( PT_BINARY, 0x0020) #define PR_ORIGINAL_EITS PROP_TAG( PT_BINARY, 0x0021) #define PR_ORIGINATOR_CERTIFICATE PROP_TAG( PT_BINARY, 0x0022) #define PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0023) #define PR_ORIGINATOR_RETURN_ADDRESS PROP_TAG( PT_BINARY, 0x0024) #define PR_PARENT_KEY PROP_TAG( PT_BINARY, 0x0025) #define PR_PRIORITY PROP_TAG( PT_LONG, 0x0026) #define PR_ORIGIN_CHECK PROP_TAG( PT_BINARY, 0x0027) #define PR_PROOF_OF_SUBMISSION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0028) #define PR_READ_RECEIPT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0029) #define PR_RECEIPT_TIME PROP_TAG( PT_SYSTIME, 0x002A) #define PR_RECIPIENT_REASSIGNMENT_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x002B) #define PR_REDIRECTION_HISTORY PROP_TAG( PT_BINARY, 0x002C) #define PR_RELATED_IPMS PROP_TAG( PT_BINARY, 0x002D) #define PR_ORIGINAL_SENSITIVITY PROP_TAG( PT_LONG, 0x002E) #define PR_LANGUAGES PROP_TAG( PT_TSTRING, 0x002F) #define PR_LANGUAGES_W PROP_TAG( PT_UNICODE, 0x002F) #define PR_LANGUAGES_A PROP_TAG( PT_STRING8, 0x002F) #define PR_REPLY_TIME PROP_TAG( PT_SYSTIME, 0x0030) #define PR_REPORT_TAG PROP_TAG( PT_BINARY, 0x0031) #define PR_REPORT_TIME PROP_TAG( PT_SYSTIME, 0x0032) #define PR_RETURNED_IPM PROP_TAG( PT_BOOLEAN, 0x0033) #define PR_SECURITY PROP_TAG( PT_LONG, 0x0034) #define PR_INCOMPLETE_COPY PROP_TAG( PT_BOOLEAN, 0x0035) #define PR_SENSITIVITY PROP_TAG( PT_LONG, 0x0036) #define PR_SUBJECT PROP_TAG( PT_TSTRING, 0x0037) #define PR_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0037) #define PR_SUBJECT_A PROP_TAG( PT_STRING8, 0x0037) #define PR_SUBJECT_IPM PROP_TAG( PT_BINARY, 0x0038) #define PR_CLIENT_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0039) #define PR_REPORT_NAME PROP_TAG( PT_TSTRING, 0x003A) #define PR_REPORT_NAME_W PROP_TAG( PT_UNICODE, 0x003A) #define PR_REPORT_NAME_A PROP_TAG( PT_STRING8, 0x003A) #define PR_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x003B) #define PR_X400_CONTENT_TYPE PROP_TAG( PT_BINARY, 0x003C) #define PR_SUBJECT_PREFIX PROP_TAG( PT_TSTRING, 0x003D) #define PR_SUBJECT_PREFIX_W PROP_TAG( PT_UNICODE, 0x003D) #define PR_SUBJECT_PREFIX_A PROP_TAG( PT_STRING8, 0x003D) #define PR_NON_RECEIPT_REASON PROP_TAG( PT_LONG, 0x003E) #define PR_RECEIVED_BY_ENTRYID PROP_TAG( PT_BINARY, 0x003F) #define PR_RECEIVED_BY_NAME PROP_TAG( PT_TSTRING, 0x0040) #define PR_RECEIVED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x0040) #define PR_RECEIVED_BY_NAME_A PROP_TAG( PT_STRING8, 0x0040) #define PR_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0041) #define PR_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0042) #define PR_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0042) #define PR_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0042) #define PR_RCVD_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0043) #define PR_RCVD_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0044) #define PR_RCVD_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0044) #define PR_RCVD_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0044) #define PR_REPORT_ENTRYID PROP_TAG( PT_BINARY, 0x0045) #define PR_READ_RECEIPT_ENTRYID PROP_TAG( PT_BINARY, 0x0046) #define PR_MESSAGE_SUBMISSION_ID PROP_TAG( PT_BINARY, 0x0047) #define PR_PROVIDER_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0048) #define PR_ORIGINAL_SUBJECT PROP_TAG( PT_TSTRING, 0x0049) #define PR_ORIGINAL_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0049) #define PR_ORIGINAL_SUBJECT_A PROP_TAG( PT_STRING8, 0x0049) #define PR_DISC_VAL PROP_TAG( PT_BOOLEAN, 0x004A) #define PR_ORIG_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x004B) #define PR_ORIG_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x004B) #define PR_ORIG_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x004B) #define PR_ORIGINAL_AUTHOR_ENTRYID PROP_TAG( PT_BINARY, 0x004C) #define PR_ORIGINAL_AUTHOR_NAME PROP_TAG( PT_TSTRING, 0x004D) #define PR_ORIGINAL_AUTHOR_NAME_W PROP_TAG( PT_UNICODE, 0x004D) #define PR_ORIGINAL_AUTHOR_NAME_A PROP_TAG( PT_STRING8, 0x004D) #define PR_ORIGINAL_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x004E) #define PR_REPLY_RECIPIENT_ENTRIES PROP_TAG( PT_BINARY, 0x004F) #define PR_REPLY_RECIPIENT_NAMES PROP_TAG( PT_TSTRING, 0x0050) #define PR_REPLY_RECIPIENT_NAMES_W PROP_TAG( PT_UNICODE, 0x0050) #define PR_REPLY_RECIPIENT_NAMES_A PROP_TAG( PT_STRING8, 0x0050) #define PR_RECEIVED_BY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0051) #define PR_RCVD_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0052) #define PR_READ_RECEIPT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0053) #define PR_REPORT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0054) #define PR_ORIGINAL_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0055) #define PR_ORIGINAL_AUTHOR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0056) #define PR_MESSAGE_TO_ME PROP_TAG( PT_BOOLEAN, 0x0057) #define PR_MESSAGE_CC_ME PROP_TAG( PT_BOOLEAN, 0x0058) #define PR_MESSAGE_RECIP_ME PROP_TAG( PT_BOOLEAN, 0x0059) #define PR_ORIGINAL_SENDER_NAME PROP_TAG( PT_TSTRING, 0x005A) #define PR_ORIGINAL_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x005A) #define PR_ORIGINAL_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x005A) #define PR_ORIGINAL_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x005B) #define PR_ORIGINAL_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005C) #define PR_ORIGINAL_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x005D) #define PR_ORIGINAL_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x005D) #define PR_ORIGINAL_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x005D) #define PR_ORIGINAL_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x005E) #define PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005F) #define PR_START_DATE PROP_TAG( PT_SYSTIME, 0x0060) #define PR_END_DATE PROP_TAG( PT_SYSTIME, 0x0061) #define PR_OWNER_APPT_ID PROP_TAG( PT_LONG, 0x0062) #define PR_RESPONSE_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0063) #define PR_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0064) #define PR_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0064) #define PR_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0064) #define PR_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0065) #define PR_SENT_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0065) #define PR_SENT_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0065) #define PR_ORIGINAL_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0066) #define PR_ORIGINAL_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0066) #define PR_ORIGINAL_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0066) #define PR_ORIGINAL_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0067) #define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0067) #define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0067) #define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0068) #define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0068) #define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0068) #define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0069) #define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0069) #define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0069) #define PR_CONVERSATION_TOPIC PROP_TAG( PT_TSTRING, 0x0070) #define PR_CONVERSATION_TOPIC_W PROP_TAG( PT_UNICODE, 0x0070) #define PR_CONVERSATION_TOPIC_A PROP_TAG( PT_STRING8, 0x0070) #define PR_CONVERSATION_INDEX PROP_TAG( PT_BINARY, 0x0071) #define PR_ORIGINAL_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0072) #define PR_ORIGINAL_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0072) #define PR_ORIGINAL_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0072) #define PR_ORIGINAL_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0073) #define PR_ORIGINAL_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0073) #define PR_ORIGINAL_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0073) #define PR_ORIGINAL_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0074) #define PR_ORIGINAL_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0074) #define PR_ORIGINAL_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0074) #define PR_RECEIVED_BY_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0075) #define PR_RECEIVED_BY_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0075) #define PR_RECEIVED_BY_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0075) #define PR_RECEIVED_BY_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0076) #define PR_RECEIVED_BY_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0076) #define PR_RECEIVED_BY_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0076) #define PR_RCVD_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0077) #define PR_RCVD_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0077) #define PR_RCVD_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0077) #define PR_RCVD_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0078) #define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0078) #define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0078) #define PR_ORIGINAL_AUTHOR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0079) #define PR_ORIGINAL_AUTHOR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0079) #define PR_ORIGINAL_AUTHOR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0079) #define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x007A) #define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x007A) #define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x007A) #define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE PROP_TAG( PT_TSTRING, 0x007B) #define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x007B) #define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x007B) #define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x007C) #define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x007C) #define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x007C) #define PR_TRANSPORT_MESSAGE_HEADERS PROP_TAG(PT_TSTRING, 0x007D) #define PR_TRANSPORT_MESSAGE_HEADERS_W PROP_TAG(PT_UNICODE, 0x007D) #define PR_TRANSPORT_MESSAGE_HEADERS_A PROP_TAG(PT_STRING8, 0x007D) #define PR_DELEGATION PROP_TAG(PT_BINARY, 0x007E) #define PR_TNEF_CORRELATION_KEY PROP_TAG(PT_BINARY, 0x007F) /* * Message content properties */ #define PR_BODY PROP_TAG( PT_TSTRING, 0x1000) #define PR_BODY_W PROP_TAG( PT_UNICODE, 0x1000) #define PR_BODY_A PROP_TAG( PT_STRING8, 0x1000) #define PR_REPORT_TEXT PROP_TAG( PT_TSTRING, 0x1001) #define PR_REPORT_TEXT_W PROP_TAG( PT_UNICODE, 0x1001) #define PR_REPORT_TEXT_A PROP_TAG( PT_STRING8, 0x1001) #define PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x1002) #define PR_REPORTING_DL_NAME PROP_TAG( PT_BINARY, 0x1003) #define PR_REPORTING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x1004) /* Removed PR_REPORT_ORIGIN_AUTHENTICATION_CHECK with DCR 3865, use PR_ORIGIN_CHECK */ #define PR_RTF_SYNC_BODY_CRC PROP_TAG( PT_LONG, 0x1006) #define PR_RTF_SYNC_BODY_COUNT PROP_TAG( PT_LONG, 0x1007) #define PR_RTF_SYNC_BODY_TAG PROP_TAG( PT_TSTRING, 0x1008) #define PR_RTF_SYNC_BODY_TAG_W PROP_TAG( PT_UNICODE, 0x1008) #define PR_RTF_SYNC_BODY_TAG_A PROP_TAG( PT_STRING8, 0x1008) #define PR_RTF_COMPRESSED PROP_TAG( PT_BINARY, 0x1009) #define PR_RTF_SYNC_PREFIX_COUNT PROP_TAG( PT_LONG, 0x1010) #define PR_RTF_SYNC_TRAILING_COUNT PROP_TAG( PT_LONG, 0x1011) #define PR_ORIGINALLY_INTENDED_RECIP_ENTRYID PROP_TAG( PT_BINARY, 0x1012) /* * Reserved 0x1100-0x1200 */ /* * Message recipient properties */ #define PR_CONTENT_INTEGRITY_CHECK PROP_TAG( PT_BINARY, 0x0C00) #define PR_EXPLICIT_CONVERSION PROP_TAG( PT_LONG, 0x0C01) #define PR_IPM_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C02) #define PR_MESSAGE_TOKEN PROP_TAG( PT_BINARY, 0x0C03) #define PR_NDR_REASON_CODE PROP_TAG( PT_LONG, 0x0C04) #define PR_NDR_DIAG_CODE PROP_TAG( PT_LONG, 0x0C05) #define PR_NON_RECEIPT_NOTIFICATION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C06) #define PR_DELIVERY_POINT PROP_TAG( PT_LONG, 0x0C07) #define PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C08) #define PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY, 0x0C09) #define PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY PROP_TAG( PT_BOOLEAN, 0x0C0A) #define PR_PHYSICAL_DELIVERY_MODE PROP_TAG( PT_LONG, 0x0C0B) #define PR_PHYSICAL_DELIVERY_REPORT_REQUEST PROP_TAG( PT_LONG, 0x0C0C) #define PR_PHYSICAL_FORWARDING_ADDRESS PROP_TAG( PT_BINARY, 0x0C0D) #define PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C0E) #define PR_PHYSICAL_FORWARDING_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0C0F) #define PR_PHYSICAL_RENDITION_ATTRIBUTES PROP_TAG( PT_BINARY, 0x0C10) #define PR_PROOF_OF_DELIVERY PROP_TAG( PT_BINARY, 0x0C11) #define PR_PROOF_OF_DELIVERY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C12) #define PR_RECIPIENT_CERTIFICATE PROP_TAG( PT_BINARY, 0x0C13) #define PR_RECIPIENT_NUMBER_FOR_ADVICE PROP_TAG( PT_TSTRING, 0x0C14) #define PR_RECIPIENT_NUMBER_FOR_ADVICE_W PROP_TAG( PT_UNICODE, 0x0C14) #define PR_RECIPIENT_NUMBER_FOR_ADVICE_A PROP_TAG( PT_STRING8, 0x0C14) #define PR_RECIPIENT_TYPE PROP_TAG( PT_LONG, 0x0C15) #define PR_REGISTERED_MAIL_TYPE PROP_TAG( PT_LONG, 0x0C16) #define PR_REPLY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C17) #define PR_REQUESTED_DELIVERY_METHOD PROP_TAG( PT_LONG, 0x0C18) #define PR_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x0C19) #define PR_SENDER_NAME PROP_TAG( PT_TSTRING, 0x0C1A) #define PR_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x0C1A) #define PR_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x0C1A) #define PR_SUPPLEMENTARY_INFO PROP_TAG( PT_TSTRING, 0x0C1B) #define PR_SUPPLEMENTARY_INFO_W PROP_TAG( PT_UNICODE, 0x0C1B) #define PR_SUPPLEMENTARY_INFO_A PROP_TAG( PT_STRING8, 0x0C1B) #define PR_TYPE_OF_MTS_USER PROP_TAG( PT_LONG, 0x0C1C) #define PR_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0C1D) #define PR_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0C1E) #define PR_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0C1E) #define PR_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0C1E) #define PR_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0C1F) #define PR_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0C1F) #define PR_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0C1F) /* * Message non-transmittable properties */ /* * The two tags, PR_MESSAGE_RECIPIENTS and PR_MESSAGE_ATTACHMENTS, * are to be used in the exclude list passed to * IMessage::CopyTo when the caller wants either the recipients or attachments * of the message to not get copied. It is also used in the ProblemArray * return from IMessage::CopyTo when an error is encountered copying them */ #define PR_CURRENT_VERSION PROP_TAG( PT_I8, 0x0E00) #define PR_DELETE_AFTER_SUBMIT PROP_TAG( PT_BOOLEAN, 0x0E01) #define PR_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0E02) #define PR_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0E02) #define PR_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0E02) #define PR_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0E03) #define PR_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0E03) #define PR_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0E03) #define PR_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0E04) #define PR_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0E04) #define PR_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0E04) #define PR_PARENT_DISPLAY PROP_TAG( PT_TSTRING, 0x0E05) #define PR_PARENT_DISPLAY_W PROP_TAG( PT_UNICODE, 0x0E05) #define PR_PARENT_DISPLAY_A PROP_TAG( PT_STRING8, 0x0E05) #define PR_MESSAGE_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0E06) #define PR_MESSAGE_FLAGS PROP_TAG( PT_LONG, 0x0E07) #define PR_MESSAGE_SIZE PROP_TAG( PT_LONG, 0x0E08) #define PR_PARENT_ENTRYID PROP_TAG( PT_BINARY, 0x0E09) #define PR_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x0E0A) #define PR_CORRELATE PROP_TAG( PT_BOOLEAN, 0x0E0C) #define PR_CORRELATE_MTSID PROP_TAG( PT_BINARY, 0x0E0D) #define PR_DISCRETE_VALUES PROP_TAG( PT_BOOLEAN, 0x0E0E) #define PR_RESPONSIBILITY PROP_TAG( PT_BOOLEAN, 0x0E0F) #define PR_SPOOLER_STATUS PROP_TAG( PT_LONG, 0x0E10) #define PR_TRANSPORT_STATUS PROP_TAG( PT_LONG, 0x0E11) #define PR_MESSAGE_RECIPIENTS PROP_TAG( PT_OBJECT, 0x0E12) #define PR_MESSAGE_ATTACHMENTS PROP_TAG( PT_OBJECT, 0x0E13) #define PR_SUBMIT_FLAGS PROP_TAG( PT_LONG, 0x0E14) #define PR_RECIPIENT_STATUS PROP_TAG( PT_LONG, 0x0E15) #define PR_TRANSPORT_KEY PROP_TAG( PT_LONG, 0x0E16) #define PR_MSG_STATUS PROP_TAG( PT_LONG, 0x0E17) #define PR_MESSAGE_DOWNLOAD_TIME PROP_TAG( PT_LONG, 0x0E18) #define PR_CREATION_VERSION PROP_TAG( PT_I8, 0x0E19) #define PR_MODIFY_VERSION PROP_TAG( PT_I8, 0x0E1A) #define PR_HASATTACH PROP_TAG( PT_BOOLEAN, 0x0E1B) #define PR_BODY_CRC PROP_TAG( PT_LONG, 0x0E1C) #define PR_NORMALIZED_SUBJECT PROP_TAG( PT_TSTRING, 0x0E1D) #define PR_NORMALIZED_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0E1D) #define PR_NORMALIZED_SUBJECT_A PROP_TAG( PT_STRING8, 0x0E1D) #define PR_RTF_IN_SYNC PROP_TAG( PT_BOOLEAN, 0x0E1F) #define PR_ATTACH_SIZE PROP_TAG( PT_LONG, 0x0E20) #define PR_ATTACH_NUM PROP_TAG( PT_LONG, 0x0E21) #define PR_PREPROCESS PROP_TAG( PT_BOOLEAN, 0x0E22) /* PR_ORIGINAL_DISPLAY_TO, _CC, and _BCC moved to transmittible range 03/09/95 */ #define PR_ORIGINATING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x0E25) #define PR_PROOF_OF_SUBMISSION PROP_TAG( PT_BINARY, 0x0E26) /* * The range of non-message and non-recipient property IDs (0x3000 - 0x3FFF) is * further broken down into ranges to make assigning new property IDs easier. * * From To Kind of property * -------------------------------- * 3000 32FF MAPI_defined common property * 3200 33FF MAPI_defined form property * 3400 35FF MAPI_defined message store property * 3600 36FF MAPI_defined Folder or AB Container property * 3700 38FF MAPI_defined attachment property * 3900 39FF MAPI_defined address book property * 3A00 3BFF MAPI_defined mailuser property * 3C00 3CFF MAPI_defined DistList property * 3D00 3DFF MAPI_defined Profile Section property * 3E00 3EFF MAPI_defined Status property * 3F00 3FFF MAPI_defined display table property */ /* * Properties common to numerous MAPI objects. * * Those properties that can appear on messages are in the * non-transmittable range for messages. They start at the high * end of that range and work down. * * Properties that never appear on messages are defined in the common * property range (see above). */ /* * properties that are common to multiple objects (including message objects) * -- these ids are in the non-transmittable range */ #define PR_ENTRYID PROP_TAG( PT_BINARY, 0x0FFF) #define PR_OBJECT_TYPE PROP_TAG( PT_LONG, 0x0FFE) #define PR_ICON PROP_TAG( PT_BINARY, 0x0FFD) #define PR_MINI_ICON PROP_TAG( PT_BINARY, 0x0FFC) #define PR_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x0FFB) #define PR_STORE_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FFA) #define PR_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FF9) #define PR_MAPPING_SIGNATURE PROP_TAG( PT_BINARY, 0x0FF8) #define PR_ACCESS_LEVEL PROP_TAG( PT_LONG, 0x0FF7) #define PR_INSTANCE_KEY PROP_TAG( PT_BINARY, 0x0FF6) #define PR_ROW_TYPE PROP_TAG( PT_LONG, 0x0FF5) #define PR_ACCESS PROP_TAG( PT_LONG, 0x0FF4) /* * properties that are common to multiple objects (usually not including message objects) * -- these ids are in the transmittable range */ #define PR_ROWID PROP_TAG( PT_LONG, 0x3000) #define PR_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3001) #define PR_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3001) #define PR_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3001) #define PR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x3002) #define PR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x3002) #define PR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x3002) #define PR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x3003) #define PR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3003) #define PR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3003) #define PR_COMMENT PROP_TAG( PT_TSTRING, 0x3004) #define PR_COMMENT_W PROP_TAG( PT_UNICODE, 0x3004) #define PR_COMMENT_A PROP_TAG( PT_STRING8, 0x3004) #define PR_DEPTH PROP_TAG( PT_LONG, 0x3005) #define PR_PROVIDER_DISPLAY PROP_TAG( PT_TSTRING, 0x3006) #define PR_PROVIDER_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3006) #define PR_PROVIDER_DISPLAY_A PROP_TAG( PT_STRING8, 0x3006) #define PR_CREATION_TIME PROP_TAG( PT_SYSTIME, 0x3007) #define PR_LAST_MODIFICATION_TIME PROP_TAG( PT_SYSTIME, 0x3008) #define PR_RESOURCE_FLAGS PROP_TAG( PT_LONG, 0x3009) #define PR_PROVIDER_DLL_NAME PROP_TAG( PT_TSTRING, 0x300A) #define PR_PROVIDER_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x300A) #define PR_PROVIDER_DLL_NAME_A PROP_TAG( PT_STRING8, 0x300A) #define PR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x300B) #define PR_PROVIDER_UID PROP_TAG( PT_BINARY, 0x300C) #define PR_PROVIDER_ORDINAL PROP_TAG( PT_LONG, 0x300D) /* * MAPI Form properties */ #define PR_FORM_VERSION PROP_TAG(PT_TSTRING, 0x3301) #define PR_FORM_VERSION_W PROP_TAG(PT_UNICODE, 0x3301) #define PR_FORM_VERSION_A PROP_TAG(PT_STRING8, 0x3301) #define PR_FORM_CLSID PROP_TAG(PT_CLSID, 0x3302) #define PR_FORM_CONTACT_NAME PROP_TAG(PT_TSTRING, 0x3303) #define PR_FORM_CONTACT_NAME_W PROP_TAG(PT_UNICODE, 0x3303) #define PR_FORM_CONTACT_NAME_A PROP_TAG(PT_STRING8, 0x3303) #define PR_FORM_CATEGORY PROP_TAG(PT_TSTRING, 0x3304) #define PR_FORM_CATEGORY_W PROP_TAG(PT_UNICODE, 0x3304) #define PR_FORM_CATEGORY_A PROP_TAG(PT_STRING8, 0x3304) #define PR_FORM_CATEGORY_SUB PROP_TAG(PT_TSTRING, 0x3305) #define PR_FORM_CATEGORY_SUB_W PROP_TAG(PT_UNICODE, 0x3305) #define PR_FORM_CATEGORY_SUB_A PROP_TAG(PT_STRING8, 0x3305) #define PR_FORM_HOST_MAP PROP_TAG(PT_MV_LONG, 0x3306) #define PR_FORM_HIDDEN PROP_TAG(PT_BOOLEAN, 0x3307) #define PR_FORM_DESIGNER_NAME PROP_TAG(PT_TSTRING, 0x3308) #define PR_FORM_DESIGNER_NAME_W PROP_TAG(PT_UNICODE, 0x3308) #define PR_FORM_DESIGNER_NAME_A PROP_TAG(PT_STRING8, 0x3308) #define PR_FORM_DESIGNER_GUID PROP_TAG(PT_CLSID, 0x3309) #define PR_FORM_MESSAGE_BEHAVIOR PROP_TAG(PT_LONG, 0x330A) /* * Message store properties */ #define PR_DEFAULT_STORE PROP_TAG( PT_BOOLEAN, 0x3400) #define PR_STORE_SUPPORT_MASK PROP_TAG( PT_LONG, 0x340D) #define PR_STORE_STATE PROP_TAG( PT_LONG, 0x340E) #define PR_IPM_SUBTREE_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3410) #define PR_IPM_OUTBOX_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3411) #define PR_IPM_WASTEBASKET_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3412) #define PR_IPM_SENTMAIL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3413) #define PR_MDB_PROVIDER PROP_TAG( PT_BINARY, 0x3414) #define PR_RECEIVE_FOLDER_SETTINGS PROP_TAG( PT_OBJECT, 0x3415) #define PR_VALID_FOLDER_MASK PROP_TAG( PT_LONG, 0x35DF) #define PR_IPM_SUBTREE_ENTRYID PROP_TAG( PT_BINARY, 0x35E0) #define PR_IPM_OUTBOX_ENTRYID PROP_TAG( PT_BINARY, 0x35E2) #define PR_IPM_WASTEBASKET_ENTRYID PROP_TAG( PT_BINARY, 0x35E3) #define PR_IPM_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x35E4) #define PR_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E5) #define PR_COMMON_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E6) #define PR_FINDER_ENTRYID PROP_TAG( PT_BINARY, 0x35E7) /* Proptags 0x35E8-0x35FF reserved for folders "guaranteed" by PR_VALID_FOLDER_MASK */ /* * Folder and AB Container properties */ #define PR_CONTAINER_FLAGS PROP_TAG( PT_LONG, 0x3600) #define PR_FOLDER_TYPE PROP_TAG( PT_LONG, 0x3601) #define PR_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3602) #define PR_CONTENT_UNREAD PROP_TAG( PT_LONG, 0x3603) #define PR_CREATE_TEMPLATES PROP_TAG( PT_OBJECT, 0x3604) #define PR_DETAILS_TABLE PROP_TAG( PT_OBJECT, 0x3605) #define PR_SEARCH PROP_TAG( PT_OBJECT, 0x3607) #define PR_SELECTABLE PROP_TAG( PT_BOOLEAN, 0x3609) #define PR_SUBFOLDERS PROP_TAG( PT_BOOLEAN, 0x360A) #define PR_STATUS PROP_TAG( PT_LONG, 0x360B) #define PR_ANR PROP_TAG( PT_TSTRING, 0x360C) #define PR_ANR_W PROP_TAG( PT_UNICODE, 0x360C) #define PR_ANR_A PROP_TAG( PT_STRING8, 0x360C) #define PR_CONTENTS_SORT_ORDER PROP_TAG( PT_MV_LONG, 0x360D) #define PR_CONTAINER_HIERARCHY PROP_TAG( PT_OBJECT, 0x360E) #define PR_CONTAINER_CONTENTS PROP_TAG( PT_OBJECT, 0x360F) #define PR_FOLDER_ASSOCIATED_CONTENTS PROP_TAG( PT_OBJECT, 0x3610) #define PR_DEF_CREATE_DL PROP_TAG( PT_BINARY, 0x3611) #define PR_DEF_CREATE_MAILUSER PROP_TAG( PT_BINARY, 0x3612) #define PR_CONTAINER_CLASS PROP_TAG( PT_TSTRING, 0x3613) #define PR_CONTAINER_CLASS_W PROP_TAG( PT_UNICODE, 0x3613) #define PR_CONTAINER_CLASS_A PROP_TAG( PT_STRING8, 0x3613) #define PR_CONTAINER_MODIFY_VERSION PROP_TAG( PT_I8, 0x3614) #define PR_AB_PROVIDER_ID PROP_TAG( PT_BINARY, 0x3615) #define PR_DEFAULT_VIEW_ENTRYID PROP_TAG( PT_BINARY, 0x3616) #define PR_ASSOC_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3617) /* Reserved 0x36C0-0x36FF */ /* * Attachment properties */ #define PR_ATTACHMENT_X400_PARAMETERS PROP_TAG( PT_BINARY, 0x3700) #define PR_ATTACH_DATA_OBJ PROP_TAG( PT_OBJECT, 0x3701) #define PR_ATTACH_DATA_BIN PROP_TAG( PT_BINARY, 0x3701) #define PR_ATTACH_ENCODING PROP_TAG( PT_BINARY, 0x3702) #define PR_ATTACH_EXTENSION PROP_TAG( PT_TSTRING, 0x3703) #define PR_ATTACH_EXTENSION_W PROP_TAG( PT_UNICODE, 0x3703) #define PR_ATTACH_EXTENSION_A PROP_TAG( PT_STRING8, 0x3703) #define PR_ATTACH_FILENAME PROP_TAG( PT_TSTRING, 0x3704) #define PR_ATTACH_FILENAME_W PROP_TAG( PT_UNICODE, 0x3704) #define PR_ATTACH_FILENAME_A PROP_TAG( PT_STRING8, 0x3704) #define PR_ATTACH_METHOD PROP_TAG( PT_LONG, 0x3705) #define PR_ATTACH_LONG_FILENAME PROP_TAG( PT_TSTRING, 0x3707) #define PR_ATTACH_LONG_FILENAME_W PROP_TAG( PT_UNICODE, 0x3707) #define PR_ATTACH_LONG_FILENAME_A PROP_TAG( PT_STRING8, 0x3707) #define PR_ATTACH_PATHNAME PROP_TAG( PT_TSTRING, 0x3708) #define PR_ATTACH_PATHNAME_W PROP_TAG( PT_UNICODE, 0x3708) #define PR_ATTACH_PATHNAME_A PROP_TAG( PT_STRING8, 0x3708) #define PR_ATTACH_RENDERING PROP_TAG( PT_BINARY, 0x3709) #define PR_ATTACH_TAG PROP_TAG( PT_BINARY, 0x370A) #define PR_RENDERING_POSITION PROP_TAG( PT_LONG, 0x370B) #define PR_ATTACH_TRANSPORT_NAME PROP_TAG( PT_TSTRING, 0x370C) #define PR_ATTACH_TRANSPORT_NAME_W PROP_TAG( PT_UNICODE, 0x370C) #define PR_ATTACH_TRANSPORT_NAME_A PROP_TAG( PT_STRING8, 0x370C) #define PR_ATTACH_LONG_PATHNAME PROP_TAG( PT_TSTRING, 0x370D) #define PR_ATTACH_LONG_PATHNAME_W PROP_TAG( PT_UNICODE, 0x370D) #define PR_ATTACH_LONG_PATHNAME_A PROP_TAG( PT_STRING8, 0x370D) #define PR_ATTACH_MIME_TAG PROP_TAG( PT_TSTRING, 0x370E) #define PR_ATTACH_MIME_TAG_W PROP_TAG( PT_UNICODE, 0x370E) #define PR_ATTACH_MIME_TAG_A PROP_TAG( PT_STRING8, 0x370E) #define PR_ATTACH_ADDITIONAL_INFO PROP_TAG( PT_BINARY, 0x370F) /* * AB Object properties */ #define PR_DISPLAY_TYPE PROP_TAG( PT_LONG, 0x3900) #define PR_TEMPLATEID PROP_TAG( PT_BINARY, 0x3902) #define PR_PRIMARY_CAPABILITY PROP_TAG( PT_BINARY, 0x3904) /* * Mail user properties */ #define PR_7BIT_DISPLAY_NAME PROP_TAG( PT_STRING8, 0x39FF) #define PR_ACCOUNT PROP_TAG( PT_TSTRING, 0x3A00) #define PR_ACCOUNT_W PROP_TAG( PT_UNICODE, 0x3A00) #define PR_ACCOUNT_A PROP_TAG( PT_STRING8, 0x3A00) #define PR_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY, 0x3A01) #define PR_CALLBACK_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A02) #define PR_CALLBACK_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A02) #define PR_CALLBACK_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A02) #define PR_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x3A03) #define PR_DISCLOSE_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x3A04) #define PR_GENERATION PROP_TAG( PT_TSTRING, 0x3A05) #define PR_GENERATION_W PROP_TAG( PT_UNICODE, 0x3A05) #define PR_GENERATION_A PROP_TAG( PT_STRING8, 0x3A05) #define PR_GIVEN_NAME PROP_TAG( PT_TSTRING, 0x3A06) #define PR_GIVEN_NAME_W PROP_TAG( PT_UNICODE, 0x3A06) #define PR_GIVEN_NAME_A PROP_TAG( PT_STRING8, 0x3A06) #define PR_GOVERNMENT_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A07) #define PR_GOVERNMENT_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A07) #define PR_GOVERNMENT_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A07) #define PR_BUSINESS_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A08) #define PR_BUSINESS_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A08) #define PR_BUSINESS_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A08) #define PR_OFFICE_TELEPHONE_NUMBER PR_BUSINESS_TELEPHONE_NUMBER #define PR_OFFICE_TELEPHONE_NUMBER_W PR_BUSINESS_TELEPHONE_NUMBER_W #define PR_OFFICE_TELEPHONE_NUMBER_A PR_BUSINESS_TELEPHONE_NUMBER_A #define PR_HOME_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A09) #define PR_HOME_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A09) #define PR_HOME_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A09) #define PR_INITIALS PROP_TAG( PT_TSTRING, 0x3A0A) #define PR_INITIALS_W PROP_TAG( PT_UNICODE, 0x3A0A) #define PR_INITIALS_A PROP_TAG( PT_STRING8, 0x3A0A) #define PR_KEYWORD PROP_TAG( PT_TSTRING, 0x3A0B) #define PR_KEYWORD_W PROP_TAG( PT_UNICODE, 0x3A0B) #define PR_KEYWORD_A PROP_TAG( PT_STRING8, 0x3A0B) #define PR_LANGUAGE PROP_TAG( PT_TSTRING, 0x3A0C) #define PR_LANGUAGE_W PROP_TAG( PT_UNICODE, 0x3A0C) #define PR_LANGUAGE_A PROP_TAG( PT_STRING8, 0x3A0C) #define PR_LOCATION PROP_TAG( PT_TSTRING, 0x3A0D) #define PR_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A0D) #define PR_LOCATION_A PROP_TAG( PT_STRING8, 0x3A0D) #define PR_MAIL_PERMISSION PROP_TAG( PT_BOOLEAN, 0x3A0E) #define PR_MHS_COMMON_NAME PROP_TAG( PT_TSTRING, 0x3A0F) #define PR_MHS_COMMON_NAME_W PROP_TAG( PT_UNICODE, 0x3A0F) #define PR_MHS_COMMON_NAME_A PROP_TAG( PT_STRING8, 0x3A0F) #define PR_ORGANIZATIONAL_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A10) #define PR_ORGANIZATIONAL_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A10) #define PR_ORGANIZATIONAL_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A10) #define PR_SURNAME PROP_TAG( PT_TSTRING, 0x3A11) #define PR_SURNAME_W PROP_TAG( PT_UNICODE, 0x3A11) #define PR_SURNAME_A PROP_TAG( PT_STRING8, 0x3A11) #define PR_ORIGINAL_ENTRYID PROP_TAG( PT_BINARY, 0x3A12) #define PR_ORIGINAL_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A13) #define PR_ORIGINAL_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A13) #define PR_ORIGINAL_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A13) #define PR_ORIGINAL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3A14) #define PR_POSTAL_ADDRESS PROP_TAG( PT_TSTRING, 0x3A15) #define PR_POSTAL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A15) #define PR_POSTAL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A15) #define PR_COMPANY_NAME PROP_TAG( PT_TSTRING, 0x3A16) #define PR_COMPANY_NAME_W PROP_TAG( PT_UNICODE, 0x3A16) #define PR_COMPANY_NAME_A PROP_TAG( PT_STRING8, 0x3A16) #define PR_TITLE PROP_TAG( PT_TSTRING, 0x3A17) #define PR_TITLE_W PROP_TAG( PT_UNICODE, 0x3A17) #define PR_TITLE_A PROP_TAG( PT_STRING8, 0x3A17) #define PR_DEPARTMENT_NAME PROP_TAG( PT_TSTRING, 0x3A18) #define PR_DEPARTMENT_NAME_W PROP_TAG( PT_UNICODE, 0x3A18) #define PR_DEPARTMENT_NAME_A PROP_TAG( PT_STRING8, 0x3A18) #define PR_OFFICE_LOCATION PROP_TAG( PT_TSTRING, 0x3A19) #define PR_OFFICE_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A19) #define PR_OFFICE_LOCATION_A PROP_TAG( PT_STRING8, 0x3A19) #define PR_PRIMARY_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1A) #define PR_PRIMARY_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1A) #define PR_PRIMARY_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1A) #define PR_BUSINESS2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1B) #define PR_BUSINESS2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1B) #define PR_BUSINESS2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1B) #define PR_OFFICE2_TELEPHONE_NUMBER PR_BUSINESS2_TELEPHONE_NUMBER #define PR_OFFICE2_TELEPHONE_NUMBER_W PR_BUSINESS2_TELEPHONE_NUMBER_W #define PR_OFFICE2_TELEPHONE_NUMBER_A PR_BUSINESS2_TELEPHONE_NUMBER_A #define PR_MOBILE_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1C) #define PR_MOBILE_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1C) #define PR_MOBILE_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1C) #define PR_CELLULAR_TELEPHONE_NUMBER PR_MOBILE_TELEPHONE_NUMBER #define PR_CELLULAR_TELEPHONE_NUMBER_W PR_MOBILE_TELEPHONE_NUMBER_W #define PR_CELLULAR_TELEPHONE_NUMBER_A PR_MOBILE_TELEPHONE_NUMBER_A #define PR_RADIO_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1D) #define PR_RADIO_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1D) #define PR_RADIO_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1D) #define PR_CAR_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1E) #define PR_CAR_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1E) #define PR_CAR_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1E) #define PR_OTHER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1F) #define PR_OTHER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1F) #define PR_OTHER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1F) #define PR_TRANSMITABLE_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A20) #define PR_TRANSMITABLE_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A20) #define PR_TRANSMITABLE_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A20) #define PR_PAGER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A21) #define PR_PAGER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A21) #define PR_PAGER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A21) #define PR_BEEPER_TELEPHONE_NUMBER PR_PAGER_TELEPHONE_NUMBER #define PR_BEEPER_TELEPHONE_NUMBER_W PR_PAGER_TELEPHONE_NUMBER_W #define PR_BEEPER_TELEPHONE_NUMBER_A PR_PAGER_TELEPHONE_NUMBER_A #define PR_USER_CERTIFICATE PROP_TAG( PT_BINARY, 0x3A22) #define PR_PRIMARY_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A23) #define PR_PRIMARY_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A23) #define PR_PRIMARY_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A23) #define PR_BUSINESS_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A24) #define PR_BUSINESS_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A24) #define PR_BUSINESS_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A24) #define PR_HOME_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A25) #define PR_HOME_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A25) #define PR_HOME_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A25) #define PR_COUNTRY PROP_TAG( PT_TSTRING, 0x3A26) #define PR_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A26) #define PR_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A26) #define PR_BUSINESS_ADDRESS_COUNTRY PR_COUNTRY #define PR_BUSINESS_ADDRESS_COUNTRY_W PR_COUNTRY_W #define PR_BUSINESS_ADDRESS_COUNTRY_A PR_COUNTRY_A #define PR_LOCALITY PROP_TAG( PT_TSTRING, 0x3A27) #define PR_LOCALITY_W PROP_TAG( PT_UNICODE, 0x3A27) #define PR_LOCALITY_A PROP_TAG( PT_STRING8, 0x3A27) #define PR_BUSINESS_ADDRESS_CITY PR_LOCALITY #define PR_BUSINESS_ADDRESS_CITY_W PR_LOCALITY_W #define PR_BUSINESS_ADDRESS_CITY_A PR_LOCALITY_A #define PR_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A28) #define PR_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A28) #define PR_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A28) #define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE PR_STATE_OR_PROVINCE #define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W PR_STATE_OR_PROVINCE_W #define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A PR_STATE_OR_PROVINCE_A #define PR_STREET_ADDRESS PROP_TAG( PT_TSTRING, 0x3A29) #define PR_STREET_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A29) #define PR_STREET_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A29) #define PR_BUSINESS_ADDRESS_STREET PR_STREET_ADDRESS #define PR_BUSINESS_ADDRESS_STREET_W PR_STREET_ADDRESS_W #define PR_BUSINESS_ADDRESS_STREET_A PR_STREET_ADDRESS_A #define PR_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A2A) #define PR_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A2A) #define PR_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A2A) #define PR_BUSINESS_ADDRESS_POSTAL_CODE PR_POSTAL_CODE #define PR_BUSINESS_ADDRESS_POSTAL_CODE_W PR_POSTAL_CODE_W #define PR_BUSINESS_ADDRESS_POSTAL_CODE_A PR_POSTAL_CODE_A #define PR_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A2B) #define PR_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A2B) #define PR_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A2B) #define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX PR_POST_OFFICE_BOX #define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_W PR_POST_OFFICE_BOX_W #define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_A PR_POST_OFFICE_BOX_A #define PR_TELEX_NUMBER PROP_TAG( PT_TSTRING, 0x3A2C) #define PR_TELEX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2C) #define PR_TELEX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2C) #define PR_ISDN_NUMBER PROP_TAG( PT_TSTRING, 0x3A2D) #define PR_ISDN_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2D) #define PR_ISDN_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2D) #define PR_ASSISTANT_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2E) #define PR_ASSISTANT_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2E) #define PR_ASSISTANT_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2E) #define PR_HOME2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2F) #define PR_HOME2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2F) #define PR_HOME2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2F) #define PR_ASSISTANT PROP_TAG( PT_TSTRING, 0x3A30) #define PR_ASSISTANT_W PROP_TAG( PT_UNICODE, 0x3A30) #define PR_ASSISTANT_A PROP_TAG( PT_STRING8, 0x3A30) #define PR_SEND_RICH_INFO PROP_TAG( PT_BOOLEAN, 0x3A40) #define PR_WEDDING_ANNIVERSARY PROP_TAG( PT_SYSTIME, 0x3A41) #define PR_BIRTHDAY PROP_TAG( PT_SYSTIME, 0x3A42) #define PR_HOBBIES PROP_TAG( PT_TSTRING, 0x3A43) #define PR_HOBBIES_W PROP_TAG( PT_UNICODE, 0x3A43) #define PR_HOBBIES_A PROP_TAG( PT_STRING8, 0x3A43) #define PR_MIDDLE_NAME PROP_TAG( PT_TSTRING, 0x3A44) #define PR_MIDDLE_NAME_W PROP_TAG( PT_UNICODE, 0x3A44) #define PR_MIDDLE_NAME_A PROP_TAG( PT_STRING8, 0x3A44) #define PR_DISPLAY_NAME_PREFIX PROP_TAG( PT_TSTRING, 0x3A45) #define PR_DISPLAY_NAME_PREFIX_W PROP_TAG( PT_UNICODE, 0x3A45) #define PR_DISPLAY_NAME_PREFIX_A PROP_TAG( PT_STRING8, 0x3A45) #define PR_PROFESSION PROP_TAG( PT_TSTRING, 0x3A46) #define PR_PROFESSION_W PROP_TAG( PT_UNICODE, 0x3A46) #define PR_PROFESSION_A PROP_TAG( PT_STRING8, 0x3A46) #define PR_PREFERRED_BY_NAME PROP_TAG( PT_TSTRING, 0x3A47) #define PR_PREFERRED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x3A47) #define PR_PREFERRED_BY_NAME_A PROP_TAG( PT_STRING8, 0x3A47) #define PR_SPOUSE_NAME PROP_TAG( PT_TSTRING, 0x3A48) #define PR_SPOUSE_NAME_W PROP_TAG( PT_UNICODE, 0x3A48) #define PR_SPOUSE_NAME_A PROP_TAG( PT_STRING8, 0x3A48) #define PR_COMPUTER_NETWORK_NAME PROP_TAG( PT_TSTRING, 0x3A49) #define PR_COMPUTER_NETWORK_NAME_W PROP_TAG( PT_UNICODE, 0x3A49) #define PR_COMPUTER_NETWORK_NAME_A PROP_TAG( PT_STRING8, 0x3A49) #define PR_CUSTOMER_ID PROP_TAG( PT_TSTRING, 0x3A4A) #define PR_CUSTOMER_ID_W PROP_TAG( PT_UNICODE, 0x3A4A) #define PR_CUSTOMER_ID_A PROP_TAG( PT_STRING8, 0x3A4A) #define PR_TTYTDD_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A4B) #define PR_TTYTDD_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A4B) #define PR_TTYTDD_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A4B) #define PR_FTP_SITE PROP_TAG( PT_TSTRING, 0x3A4C) #define PR_FTP_SITE_W PROP_TAG( PT_UNICODE, 0x3A4C) #define PR_FTP_SITE_A PROP_TAG( PT_STRING8, 0x3A4C) #define PR_GENDER PROP_TAG( PT_SHORT, 0x3A4D) #define PR_MANAGER_NAME PROP_TAG( PT_TSTRING, 0x3A4E) #define PR_MANAGER_NAME_W PROP_TAG( PT_UNICODE, 0x3A4E) #define PR_MANAGER_NAME_A PROP_TAG( PT_STRING8, 0x3A4E) #define PR_NICKNAME PROP_TAG( PT_TSTRING, 0x3A4F) #define PR_NICKNAME_W PROP_TAG( PT_UNICODE, 0x3A4F) #define PR_NICKNAME_A PROP_TAG( PT_STRING8, 0x3A4F) #define PR_PERSONAL_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A50) #define PR_PERSONAL_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A50) #define PR_PERSONAL_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A50) #define PR_BUSINESS_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A51) #define PR_BUSINESS_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A51) #define PR_BUSINESS_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A51) #define PR_CONTACT_VERSION PROP_TAG( PT_CLSID, 0x3A52) #define PR_CONTACT_ENTRYIDS PROP_TAG( PT_MV_BINARY, 0x3A53) #define PR_CONTACT_ADDRTYPES PROP_TAG( PT_MV_TSTRING, 0x3A54) #define PR_CONTACT_ADDRTYPES_W PROP_TAG( PT_MV_UNICODE, 0x3A54) #define PR_CONTACT_ADDRTYPES_A PROP_TAG( PT_MV_STRING8, 0x3A54) #define PR_CONTACT_DEFAULT_ADDRESS_INDEX PROP_TAG( PT_LONG, 0x3A55) #define PR_CONTACT_EMAIL_ADDRESSES PROP_TAG( PT_MV_TSTRING, 0x3A56) #define PR_CONTACT_EMAIL_ADDRESSES_W PROP_TAG( PT_MV_UNICODE, 0x3A56) #define PR_CONTACT_EMAIL_ADDRESSES_A PROP_TAG( PT_MV_STRING8, 0x3A56) #define PR_COMPANY_MAIN_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A57) #define PR_COMPANY_MAIN_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A57) #define PR_COMPANY_MAIN_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A57) #define PR_CHILDRENS_NAMES PROP_TAG( PT_MV_TSTRING, 0x3A58) #define PR_CHILDRENS_NAMES_W PROP_TAG( PT_MV_UNICODE, 0x3A58) #define PR_CHILDRENS_NAMES_A PROP_TAG( PT_MV_STRING8, 0x3A58) #define PR_HOME_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A59) #define PR_HOME_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A59) #define PR_HOME_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A59) #define PR_HOME_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A5A) #define PR_HOME_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A5A) #define PR_HOME_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A5A) #define PR_HOME_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A5B) #define PR_HOME_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A5B) #define PR_HOME_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A5B) #define PR_HOME_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A5C) #define PR_HOME_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A5C) #define PR_HOME_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A5C) #define PR_HOME_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A5D) #define PR_HOME_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A5D) #define PR_HOME_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A5D) #define PR_HOME_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A5E) #define PR_HOME_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A5E) #define PR_HOME_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A5E) #define PR_OTHER_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A5F) #define PR_OTHER_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A5F) #define PR_OTHER_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A5F) #define PR_OTHER_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A60) #define PR_OTHER_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A60) #define PR_OTHER_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A60) #define PR_OTHER_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A61) #define PR_OTHER_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A61) #define PR_OTHER_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A61) #define PR_OTHER_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A62) #define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A62) #define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A62) #define PR_OTHER_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A63) #define PR_OTHER_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A63) #define PR_OTHER_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A63) #define PR_OTHER_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A64) #define PR_OTHER_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A64) #define PR_OTHER_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A64) /* * Profile section properties */ #define PR_STORE_PROVIDERS PROP_TAG( PT_BINARY, 0x3D00) #define PR_AB_PROVIDERS PROP_TAG( PT_BINARY, 0x3D01) #define PR_TRANSPORT_PROVIDERS PROP_TAG( PT_BINARY, 0x3D02) #define PR_DEFAULT_PROFILE PROP_TAG( PT_BOOLEAN, 0x3D04) #define PR_AB_SEARCH_PATH PROP_TAG( PT_MV_BINARY, 0x3D05) #define PR_AB_DEFAULT_DIR PROP_TAG( PT_BINARY, 0x3D06) #define PR_AB_DEFAULT_PAB PROP_TAG( PT_BINARY, 0x3D07) #define PR_FILTERING_HOOKS PROP_TAG( PT_BINARY, 0x3D08) #define PR_SERVICE_NAME PROP_TAG( PT_TSTRING, 0x3D09) #define PR_SERVICE_NAME_W PROP_TAG( PT_UNICODE, 0x3D09) #define PR_SERVICE_NAME_A PROP_TAG( PT_STRING8, 0x3D09) #define PR_SERVICE_DLL_NAME PROP_TAG( PT_TSTRING, 0x3D0A) #define PR_SERVICE_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x3D0A) #define PR_SERVICE_DLL_NAME_A PROP_TAG( PT_STRING8, 0x3D0A) #define PR_SERVICE_ENTRY_NAME PROP_TAG( PT_STRING8, 0x3D0B) #define PR_SERVICE_UID PROP_TAG( PT_BINARY, 0x3D0C) #define PR_SERVICE_EXTRA_UIDS PROP_TAG( PT_BINARY, 0x3D0D) #define PR_SERVICES PROP_TAG( PT_BINARY, 0x3D0E) #define PR_SERVICE_SUPPORT_FILES PROP_TAG( PT_MV_TSTRING, 0x3D0F) #define PR_SERVICE_SUPPORT_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D0F) #define PR_SERVICE_SUPPORT_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D0F) #define PR_SERVICE_DELETE_FILES PROP_TAG( PT_MV_TSTRING, 0x3D10) #define PR_SERVICE_DELETE_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D10) #define PR_SERVICE_DELETE_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D10) #define PR_AB_SEARCH_PATH_UPDATE PROP_TAG( PT_BINARY, 0x3D11) #define PR_PROFILE_NAME PROP_TAG( PT_TSTRING, 0x3D12) #define PR_PROFILE_NAME_A PROP_TAG( PT_STRING8, 0x3D12) #define PR_PROFILE_NAME_W PROP_TAG( PT_UNICODE, 0x3D12) /* * Status object properties */ #define PR_IDENTITY_DISPLAY PROP_TAG( PT_TSTRING, 0x3E00) #define PR_IDENTITY_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3E00) #define PR_IDENTITY_DISPLAY_A PROP_TAG( PT_STRING8, 0x3E00) #define PR_IDENTITY_ENTRYID PROP_TAG( PT_BINARY, 0x3E01) #define PR_RESOURCE_METHODS PROP_TAG( PT_LONG, 0x3E02) #define PR_RESOURCE_TYPE PROP_TAG( PT_LONG, 0x3E03) #define PR_STATUS_CODE PROP_TAG( PT_LONG, 0x3E04) #define PR_IDENTITY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3E05) #define PR_OWN_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x3E06) #define PR_RESOURCE_PATH PROP_TAG( PT_TSTRING, 0x3E07) #define PR_RESOURCE_PATH_W PROP_TAG( PT_UNICODE, 0x3E07) #define PR_RESOURCE_PATH_A PROP_TAG( PT_STRING8, 0x3E07) #define PR_STATUS_STRING PROP_TAG( PT_TSTRING, 0x3E08) #define PR_STATUS_STRING_W PROP_TAG( PT_UNICODE, 0x3E08) #define PR_STATUS_STRING_A PROP_TAG( PT_STRING8, 0x3E08) #define PR_X400_DEFERRED_DELIVERY_CANCEL PROP_TAG( PT_BOOLEAN, 0x3E09) #define PR_HEADER_FOLDER_ENTRYID PROP_TAG( PT_BINARY, 0x3E0A) #define PR_REMOTE_PROGRESS PROP_TAG( PT_LONG, 0x3E0B) #define PR_REMOTE_PROGRESS_TEXT PROP_TAG( PT_TSTRING, 0x3E0C) #define PR_REMOTE_PROGRESS_TEXT_W PROP_TAG( PT_UNICODE, 0x3E0C) #define PR_REMOTE_PROGRESS_TEXT_A PROP_TAG( PT_STRING8, 0x3E0C) #define PR_REMOTE_VALIDATE_OK PROP_TAG( PT_BOOLEAN, 0x3E0D) /* * Display table properties */ #define PR_CONTROL_FLAGS PROP_TAG( PT_LONG, 0x3F00) #define PR_CONTROL_STRUCTURE PROP_TAG( PT_BINARY, 0x3F01) #define PR_CONTROL_TYPE PROP_TAG( PT_LONG, 0x3F02) #define PR_DELTAX PROP_TAG( PT_LONG, 0x3F03) #define PR_DELTAY PROP_TAG( PT_LONG, 0x3F04) #define PR_XPOS PROP_TAG( PT_LONG, 0x3F05) #define PR_YPOS PROP_TAG( PT_LONG, 0x3F06) #define PR_CONTROL_ID PROP_TAG( PT_BINARY, 0x3F07) #define PR_INITIAL_DETAILS_PANE PROP_TAG( PT_LONG, 0x3F08) /* * Secure property id range */ #define PROP_ID_SECURE_MIN 0x67F0 #define PROP_ID_SECURE_MAX 0x67FF #endif /* MAPITAGS_H */ p3scan-2.3.2/ripmime-1.4.0.6/tnef/ms-tnef.out0000644000175000001440000003427210347164530017060 0ustar jlaiusers ++++ ms-tnef ++++ TNEF_SIGNATURE (0x223E9F78), offset: 0x00000000 Attach Key -- 0x100D ATTRIBUTE: attTnefVersion (0x00089006) Component -- Message (0x01) Offset -- 0x00000006 Size -- 4 Encoding -- Tnef Version Information Version: 1.0 Computed Checksum 0x0001 Expected Checksum 0x0001 ATTRIBUTE: attOemCodepage (0x00069007) Component -- Message (0x01) Offset -- 0x00000015 Size -- 8 Encoding -- Tnef Codepage Information Codepage: Primary: 1252 Secondary: 0 Computed Checksum 0x00E8 Expected Checksum 0x00E8 ATTRIBUTE: attPriority (0x0004800D) Component -- Message (0x01) Offset -- 0x00000028 Size -- 2 Encoding -- Down-level Attribute Data: 02 00 *..* Computed Checksum 0x0002 Expected Checksum 0x0002 ATTRIBUTE: attRecipTable (0x00069004) Component -- Message (0x01) Offset -- 0x00000035 Size -- 256 Encoding -- MAPI Table No. Rows -- 1 ROW 0: No. Props -- 12 PROPERTY: PR_ROWID (0x30000003) Offset -- 0x00000046 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 03 00 00 00 *....* Local Checksum -- 0x0036 PROPERTY: PR_RESPONSIBILITY (0x0E0F000B) Offset -- 0x0000004E PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0028 PROPERTY: PR_ENTRYID (0x0FFF0102) Offset -- 0x00000056 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 59 Data (1): 00 00 00 00 81 2B 1F A4 -- BE A3 10 19 9D 6E 00 DD *.....+.......n..* 01 0F 54 02 00 00 00 00 -- 62 6C 6F 6E 67 40 75 69 *..T.....blong@ui* 75 63 2E 65 64 75 00 53 -- 4D 54 50 00 62 6C 6F 6E *uc.edu.SMTP.blon* 67 40 75 69 75 63 2E 65 -- 64 75 00 *g@uiuc.edu.* Pad -- 1 byte Local Checksum -- 0x12C0 PROPERTY: PR_ADDRTYPE (0x3002001E) Offset -- 0x0000009E PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 5 Data (1): 53 4D 54 50 00 *SMTP.* Pad -- 3 bytes Local Checksum -- 0x019A PROPERTY: PR_EMAIL_ADDRESS (0x3003001E) Offset -- 0x000000B2 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 15 Data (1): 62 6C 6F 6E 67 40 75 69 -- 75 63 2E 65 64 75 00 *blong@uiuc.edu.* Pad -- 1 byte Local Checksum -- 0x05D5 PROPERTY: PR_RECIPIENT_TYPE (0x0C150003) Offset -- 0x000000CE PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 01 00 00 00 *....* Local Checksum -- 0x0025 PROPERTY: PR_OBJECT_TYPE (0x0FFE0003) Offset -- 0x000000D6 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 06 00 00 00 *....* Local Checksum -- 0x0116 PROPERTY: PR_DISPLAY_NAME (0x3001001E) Offset -- 0x000000DE PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 17 Data (1): 27 62 6C 6F 6E 67 40 75 -- 69 75 63 2E 65 64 75 27 *'blong@uiuc.edu'* 00 *.* Pad -- 3 bytes Local Checksum -- 0x0623 PROPERTY: PR_SEARCH_KEY (0x300B0102) Offset -- 0x000000FE PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 20 Data (1): 53 4D 54 50 3A 42 4C 4F -- 4E 47 40 55 49 55 43 2E *SMTP:BLONG@UIUC.* 45 44 55 00 *EDU.* Local Checksum -- 0x05C5 PROPERTY: PR_DISPLAY_TYPE (0x39000003) Offset -- 0x0000011E PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x003C PROPERTY: PR_SEND_RICH_INFO (0x3A40000B) Offset -- 0x00000126 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 01 00 00 00 *....* Local Checksum -- 0x0086 PROPERTY: PR_INSTANCE_KEY (0x0FF60102) Offset -- 0x0000012E PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 4 Data (1): 00 00 00 03 *....* Local Checksum -- 0x0110 PROPERTIES: end ROWS: end Computed Size 256 Expected Size 256 Computed Checksum 0x298F Expected Checksum 0x298F ATTRIBUTE: attMessageClass (0x00078008) Component -- Message (0x01) Offset -- 0x00000140 Size -- 24 Encoding -- Down-level Attribute Data: 49 50 4D 2E 4D 69 63 72 -- 6F 73 6F 66 74 20 4D 61 *IPM.Microsoft Ma* 69 6C 2E 4E 6F 74 65 00 *il.Note.* Computed Checksum 0x0831 Expected Checksum 0x0831 ATTRIBUTE: attSubject (0x00018004) Component -- Message (0x01) Offset -- 0x00000163 Size -- 60 Encoding -- Down-level Attribute Data: 41 41 41 41 41 41 41 41 -- 41 41 41 41 41 52 52 52 *AAAAAAAAAAAAARRR* 52 52 52 52 52 52 47 47 -- 47 47 47 47 47 47 47 47 *RRRRRRGGGGGGGGGG* 47 47 48 48 48 48 48 48 -- 48 48 48 48 21 21 21 21 *GGHHHHHHHHHH!!!!* 21 21 21 21 21 21 21 21 -- 21 21 21 00 *!!!!!!!!!!!.* Computed Checksum 0x0E42 Expected Checksum 0x0E42 ATTRIBUTE: attDateSent (0x00038005) Component -- Message (0x01) Offset -- 0x000001AA Size -- 14 Encoding -- Down-level DTR Date: Friday, 05/23/1997, 11:29:10 CD 07 05 00 17 00 0B 00 -- 1D 00 0A 00 05 00 *..............* Computed Checksum 0x0127 Expected Checksum 0x0127 ATTRIBUTE: attDateModified (0x00038020) Component -- Message (0x01) Offset -- 0x000001C3 Size -- 14 Encoding -- Down-level DTR Date: Friday, 05/23/1997, 11:27:59 CD 07 05 00 17 00 0B 00 -- 1B 00 3B 00 05 00 *..........;...* Computed Checksum 0x0156 Expected Checksum 0x0156 ATTRIBUTE: attMessageID (0x00018009) Component -- Message (0x01) Offset -- 0x000001DC Size -- 33 Encoding -- Down-level Attribute Data: 34 32 38 35 34 32 30 43 -- 34 33 44 33 44 30 31 31 *4285420C43D3D011* 42 36 35 37 30 30 32 30 -- 41 46 41 31 41 30 30 33 *B6570020AFA1A003* 00 *.* Computed Checksum 0x06D3 Expected Checksum 0x06D3 ATTRIBUTE: attMAPIProps (0x00069003) Component -- Message (0x01) Offset -- 0x00000208 Size -- 508 Encoding -- MAPI Properties No. Props -- 18 PROPERTY: PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED (0x0023000B) Offset -- 0x00000215 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x002E PROPERTY: PR_PRIORITY (0x00260003) Offset -- 0x0000021D PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0029 PROPERTY: PR_READ_RECEIPT_REQUESTED (0x0029000B) Offset -- 0x00000225 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0034 PROPERTY: PR_SENSITIVITY (0x00360003) Offset -- 0x0000022D PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0039 PROPERTY: PR_CLIENT_SUBMIT_TIME (0x00390040) Offset -- 0x00000235 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 20 B7 AA 71 96 67 BC 01 * ..q.g..* Local Checksum -- 0x0425 PROPERTY: PR_CONVERSATION_TOPIC (0x0070001E) Offset -- 0x00000241 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 60 Data (1): 41 41 41 41 41 41 41 41 -- 41 41 41 41 41 52 52 52 *AAAAAAAAAAAAARRR* 52 52 52 52 52 52 47 47 -- 47 47 47 47 47 47 47 47 *RRRRRRGGGGGGGGGG* 47 47 48 48 48 48 48 48 -- 48 48 48 48 21 21 21 21 *GGHHHHHHHHHH!!!!* 21 21 21 21 21 21 21 21 -- 21 21 21 00 *!!!!!!!!!!!.* Local Checksum -- 0x0F0D PROPERTY: PR_CONVERSATION_INDEX (0x00710102) Offset -- 0x00000289 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 22 Data (1): 01 BC 67 96 71 A1 0C 42 -- 85 43 D3 43 11 D0 B6 57 *..g.q..B.C.C...W* 00 20 AF A1 A0 03 *. ....* Pad -- 2 bytes Local Checksum -- 0x0984 PROPERTY: PR_SENDER_ADDRTYPE (0x0C1E001E) Offset -- 0x000002AD PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 5 Data (1): 53 4D 54 50 00 *SMTP.* Pad -- 3 bytes Local Checksum -- 0x0192 PROPERTY: PR_SENDER_EMAIL_ADDRESS (0x0C1F001E) Offset -- 0x000002C1 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 19 Data (1): 6E 6F 61 68 62 61 6E 40 -- 64 76 73 77 65 62 2E 63 *noahban@dvsweb.c* 6F 6D 00 *om.* Pad -- 1 byte Local Checksum -- 0x076C PROPERTY: PR_RTF_SYNC_BODY_CRC (0x10060003) Offset -- 0x000002E1 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 3B EE 7D 8D *;.}.* Local Checksum -- 0x024C PROPERTY: PR_RTF_SYNC_BODY_COUNT (0x10070003) Offset -- 0x000002E9 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 14 00 00 00 *....* Local Checksum -- 0x002E PROPERTY: PR_RTF_SYNC_BODY_TAG (0x1008001E) Offset -- 0x000002F1 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 21 Data (1): 57 48 41 54 48 41 53 48 -- 41 50 50 45 4E 45 44 54 *WHATHASHAPPENEDT* 4F 55 53 3F 00 *OUS?.* Pad -- 3 bytes Local Checksum -- 0x062B PROPERTY: PR_RTF_COMPRESSED (0x10090102) Offset -- 0x00000315 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 180 Data (1): B0 00 00 00 9B 01 00 00 -- 4C 5A 46 75 50 49 B9 90 *........LZFuPI..* FF 00 0A 01 0F 02 15 02 -- A8 05 EB 02 83 00 50 02 *..............P.* F2 09 02 00 63 68 0A C0 -- 73 65 74 32 37 06 00 06 *....ch..set27...* C3 02 83 32 03 C5 02 00 -- 70 72 42 71 11 E2 73 74 *...2....prBq..st* 65 6D 02 83 33 F7 02 E4 -- 07 13 02 83 34 03 45 13 *em..3.......4.E.* 35 07 6D 02 80 FE 7D 0A -- 80 08 CF 09 D9 02 80 0A *5.m...}.........* 81 0D B1 0B 60 E0 6E 67 -- 31 30 33 14 50 0B 0A 15 *....`.ng103.P...* 61 F9 0B F0 34 20 0A 85 -- 1B 9F 1C AF 1D BF 1E CF *a...4 ..........* F7 1F DF 20 EF 21 F8 77 -- 11 80 05 40 11 80 04 20 *... .!.w...@... * 19 11 80 70 70 09 F0 09 -- 80 20 74 6F F0 20 75 73 *...pp.... to. us* 3F 1A AC 0A A0 03 60 13 -- D0 2E 63 05 40 0A 85 17 *?.....`...c.@...* 61 00 26 A0 *a.&.* Local Checksum -- 0x39D6 PROPERTY: PR_RTF_SYNC_PREFIX_COUNT (0x10100003) Offset -- 0x000003D5 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0023 PROPERTY: PR_RTF_SYNC_TRAILING_COUNT (0x10110003) Offset -- 0x000003DD PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0024 PROPERTY: PR_CREATION_TIME (0x30070040) Offset -- 0x000003E5 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 00 00 02 47 96 67 BC 01 *...G.g..* Local Checksum -- 0x027A PROPERTY: PR_LAST_MODIFICATION_TIME (0x30080040) Offset -- 0x000003F1 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 00 00 02 47 96 67 BC 01 *...G.g..* Local Checksum -- 0x027B PROPERTY: PR_SUBJECT_PREFIX (0x003D001E) Offset -- 0x000003FD PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 1 Data (1): 00 *.* Pad -- 3 bytes Local Checksum -- 0x005D PROPERTIES: end Computed Size 508 Expected Size 508 Computed Checksum 0x6E9E Expected Checksum 0x6E9E TNEF: end p3scan-2.3.2/ripmime-1.4.0.6/Makefile0000644000175000001440000000410610347164530015453 0ustar jlaiusers# # VERSION CHANGES # # 0.1.13: Added -I. LOCATION=/usr/local VERSION=0.1.14 # VERSION changes #--------------------- # 0.1.14: Added 'install' usage # #CC=gcc # if there isn't already a default CFLAGS set, # use our recommended settings. #CFLAGS ?= -Wall -g -O2 -Werror $(CPU_OPTS) #CFLAGS=-Wall -g -O2 -Wundef -Wshadow -Wsign-compare -I. CFLAGS=-Wall -g -O2 -I. # OLE decoding is still considered to be 'beta' mode - so it # disabled in the stable release of ripMIME for now # You can turn it on, but watch out for various office files # which may/probably-will break ripMIME with a segfault # or other strange errors. If you do wish to test this # and find a dud mailpack, please send through to # mailpacks@pldaniels.com # COMPONENTS= -DRIPOLE LIBS= #COMPONENTS= # DEBUGGING Related Flags OBJ=ripmime RIPOLE_OBJS= ripOLE/ole.o ripOLE/olestream-unwrap.o ripOLE/bytedecoders.o ripOLE/bt-int.o #RIPOLE_OBJS= OFILES= strstack.o mime.o ffget.o MIME_headers.o tnef/tnef.o rawget.o pldstr.o logger.o libmime-decoders.o boundary-stack.o uuencode.o filename-filters.o $(RIPOLE_OBJS) default: tnef/tnef.o ripmime ripOLE/ole.o buildcodes.h: ./generate-buildcodes.sh ripOLE/ole.o: ./build_ripOLE tnef/tnef.o: ./build_tnef .c.o: ${CC} ${CFLAGS} $(COMPONENTS) -c $*.c all: ${OBJ} solib: ${OFILES} ripmime-api.o gcc --shared -Wl,-soname,libripmime.so.1 ${OFILES} ripmime-api.o -o libripmime.so.1.4.0 -lc libripmime: ${OFILES} ripmime-api.o ar ruvs libripmime.a ${OFILES} ripmime-api.o ripl: ripmime.a ${CC} ${CFLAGS} ripmime.c ripmime.a -o ripmime sco: ${OFILES} ${CC} ${CFLAGS} ripmime.c ${OFILES} -o ripmime -lsocket ripmime: ${OFILES} ripmime.c buildcodes.h ${CC} ${CFLAGS} $(COMPONENTS) ripmime.c ${OFILES} -o ripmime ${LIBS} riptest: ${OFILES} ${CC} ${CFLAGS} riptest.c ${OFILES} -o riptest install: ${OBJ} strip ripmime cp ripmime ${LOCATION}/bin/ cp ripmime.1 ${LOCATION}/man/man1 clean: rm -f *.o *core ${OBJ} buildcodes.h rm -f tnef/*.o rm -f ripOLE/*.o ripOLE/ripole MIMEH: MIME_headers.o strlower.o ${CC} ${CFLAGS} MIMEH_test.c MIME_headers.o strlower.o -o MIMEH_test p3scan-2.3.2/ripmime-1.4.0.6/CONTRIBUTORS0000644000175000001440000000546110347164530015700 0ustar jlaiusersOriginal Author : Paul L Daniels --------------------------- Author : Farit Date : 14/01/2004 Contrib: Supplied various mailpacks with exploits/viruses/worms and alternative 'header' prefixing. --------------------------- Change : Correction of various spurilous messages appearing even when verbosity is not active Date : 26/01/2003 Author : Chris Hine --------------------------- Change : Change char * to unsigned char * in strlower() function to deal with non-ASCII characters ( ie, French, Chinese ) Date : 09/11/2002 Author : Emmanuel Collignon --------------------------- Change : Patch to deal with spaces inbetween name=, filename= type MIME headers. Submitted by Phil Brutsche Date : 06/11/2002 --------------------------- Change : Updates to FFGET routines. Improve handling of one-off issues and buffer boundary conditions Author : Joseph Gooch Email : Date : 27/02/2002 Notification : Version number update (I hate it when I forget that) Notifier : wes schreiner Date : 21/02/2002 --------------------------- Change : TNEF decoding improvements - Quoted Printable decoding fixes Author : Chea Chee Keong Email : ckchea@mailandnews.com Date : 20/02/2002 --------------------------- Change : CRCR logic clarification Author : Joseph Gooch Date : 18/02/2002 --------------------------- Change : TNEF decoding improvements Author : Chea Chee Keong Email : ckchea@mailandnews.com Date : 01/01/2002 --------------------------- Change : add ability to read mimepack from STDIN Author : Matthew McNaughton Date : 24/11/2000 --------------------------- Fixes: Change : Correction for handling varying line-length BASE64 encoded files Author : Trent, Michael (Xerox USA) and John Armstrong (EFI.com) Date : 05/11/2001 --------------------------- Change : Support for SGI systems (fixes lockup) Author : Don Lafontaine Date : 27/09/2001 --------------------------- Change : Correct problem where a filename will contain multiple consecutive /'s Author : James Cownie Date : 03/02/2001 --------------------------- Change : Replaced the tmpnam() call for temporary filename with MIME_tmpnam() Author : Dave DeMaagd & Paul L Daniels Date : 29/01/2001 --------------------------- Change : added patch to detected boundaries without surrounding quotations Author : Rolf Date : 12/01/2001 --------------------------- Support: Date : 07/01/2000 Person: Hans Harder What: Supplied me with a mailpack which couldn't be correctly decoded by ripMIME due to it not having a trailing CR/LF after the base64 encoded portion of the attachment. --------------------------- Date : 29/01/2001 Person : Sven Rassmaan What: Supplied me with a mailpack/spool which I could test the multiple-email-spool-extract facility (also happened to be the requestor or such) p3scan-2.3.2/ripmime-1.4.0.6/MIME_headers.c0000644000175000001440000025271410347164530016413 0ustar jlaiusers/*----------------------------------------------------------------------- ** ** ** MIME_headers ** ** Written by Paul L Daniels, originally for the Xamime project ** (http://www.xamime.com) but since spawned off to the ripMIME/alterMIME ** family of email parsing tools. ** ** Copyright PLD, 1999,2000,2001,2002,2003 ** Licence: BSD ** For more information on the licence and copyrights of this code, please ** email copyright@pldaniels.com ** CHANGES ** 2003-Jun-24: PLD: Added subject parsing ** */ #include #include #include #include #include #include #include #include #include #include "ffget.h" #include "pldstr.h" #include "libmime-decoders.h" #include "logger.h" #include "strstack.h" #include "boundary-stack.h" #include "filename-filters.h" #include "MIME_headers.h" #ifndef FL #define FL __FILE__, __LINE__ #endif // Debug precodes #define MIMEH_DPEDANTIC ((glb.debug >= _MIMEH_DEBUG_PEDANTIC)) #define MIMEH_DNORMAL ((glb.debug >= _MIMEH_DEBUG_NORMAL )) #define DMIMEH if ((glb.debug >= _MIMEH_DEBUG_NORMAL)) char *MIMEH_defect_description_array[_MIMEH_DEFECT_ARRAY_SIZE]; struct MIMEH_globals { int doubleCR; int doubleCR_save; char doubleCRname[_MIMEH_STRLEN_MAX +1]; char appledouble_filename[_MIMEH_STRLEN_MAX +1]; char subject[_MIMEH_STRLEN_MAX +1]; char *headerline; char *headerline_original; // Holds the original header-form without decoding. int save_headers; int save_headers_original; int test_mailbox; int debug; int webform; int doubleCR_count; int verbose; int verbose_contenttype; int header_longsearch; // keep searching until valid headers are found - this is used to filter out qmail bounced emails - breaks RFC's but people are wanting it :-( int longsearch_limit; // how many segments do we attempt to look ahead... char output_dir[_MIMEH_STRLEN_MAX +1]; FILE *header_file; FILE *original_header_file; int original_header_save_to_file; }; static struct MIMEH_globals glb; /*-----------------------------------------------------------------\ Function Name : MIMEH_version Returns Type : int ----Parameter List 1. void, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_version(void) { fprintf(stdout,"mimeheaders: %s\n", MIMEH_VERSION); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_init( void ) { glb.doubleCR = 0; glb.headerline = NULL; glb.headerline_original = NULL; glb.header_file = NULL; glb.original_header_file = NULL; glb.original_header_save_to_file = 0; glb.save_headers = 0; glb.save_headers_original = 0; glb.test_mailbox = 0; glb.debug = 0; glb.webform = 0; glb.doubleCR_count = 0; glb.doubleCR_save = 1; glb.verbose = 0; glb.verbose_contenttype = 0; glb.output_dir[0]='\0'; glb.doubleCRname[0]='\0'; glb.appledouble_filename[0]='\0'; glb.header_longsearch=0; glb.longsearch_limit=1; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_doubleCR Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_doubleCR( void ) { return glb.doubleCR; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_doubleCR Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_doubleCR( int level ) { glb.doubleCR = level; return glb.doubleCR; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_doubleCR_save Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_doubleCR_save( int level ) { glb.doubleCR_save = level; return glb.doubleCR_save; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_doubleCR_save Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_doubleCR_save( void ) { return glb.doubleCR_save; } /*-----------------------------------------------------------------\ Function Name : *MIMEH_get_doubleCR_name Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_doubleCR_name( void ) { return glb.doubleCRname; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_outputdir Returns Type : int ----Parameter List 1. char *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_outputdir( char *dir ) { if (dir) snprintf(glb.output_dir,_MIMEH_STRLEN_MAX,"%s",dir); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_webform Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_webform( int level ) { glb.webform = level; return glb.webform; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_mailbox Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_mailbox( int level ) { glb.test_mailbox = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_verbosity Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_verbosity( int level ) { glb.verbose = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_verbosity_contenttype( int level ) { glb.verbose_contenttype = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_verbosity_contenttype Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_verbosity_contenttype( void ) { return glb.verbose_contenttype; } /*------------------------------------------------------------------------ Procedure: MIMEH_set_headers_save ID:1 Purpose: Sets MIMEH's headers save file (where MIMEH will save the headers it reads in from the mailpack) Input: Output: Errors: ------------------------------------------------------------------------*/ int MIMEH_set_headers_save( FILE *f ) { glb.header_file = f; glb.save_headers = 1; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_original_save_to_file Returns Type : int ----Parameter List 1. FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_original_save_to_file( FILE *f ) { if (f == NULL) glb.original_header_save_to_file = 0; else glb.original_header_save_to_file = 1; glb.original_header_file = f; return glb.original_header_save_to_file; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_nosave Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_nosave( void ) { glb.header_file = NULL; glb.save_headers = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers_save Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_headers_save( void ) { return glb.save_headers; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_save_original Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_save_original( int level ) { glb.save_headers_original = level; return glb.save_headers_original; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers_ptr Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_headers_ptr( void ) { return glb.headerline; } /*-----------------------------------------------------------------\ Function Name : *MIMEH_get_headers_original_ptr Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_headers_original_ptr( void ) { return glb.headerline_original; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_header_longsearch Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: The header long-search is a facility switch that will make the header searching to continue on until it either reaches the end of the file or it finds valid (??) headers to work on. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_header_longsearch( int level ) { glb.header_longsearch = level; return glb.header_longsearch; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_defect Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. int defect , The defect code to set ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_defect( struct MIMEH_header_info *hinfo, int defect ) { if ((defect >= 0)&&(defect < _MIMEH_DEFECT_ARRAY_SIZE)) { hinfo->defects[defect]++; hinfo->header_defect_count++; DMIMEH LOGGER_log("%s:%d:MIMEH_set_defect:DEBUG: Setting defect index '%d' to '%d'",FL, defect, hinfo->defects[defect]); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_contenttype Returns Type : int ----Parameter List 1. int range_type, 2. int content_type , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_contenttype( int range_type, int content_type ) { int diff; diff = content_type -range_type; if ((diff < _CTYPE_RANGE)&&(diff > 0)) return 1; else return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_binary Returns Type : int ----Parameter List 1. struct FFGET_FILE *f , ------------------ Exit Codes : 1 = yes, it's binary, 0 = no. Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_binary( char *fname ) { char buffer[1024]; int read_count; FILE *f; f = fopen(fname,"r"); if (!f) return 1; read_count = fread(buffer, 1, 1024, f); fclose(f); while (read_count) { read_count--; if (buffer[read_count] == 0) return 1; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_are_headers_RFC822 Returns Type : int ----Parameter List 1. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_are_headers_RFC822( char *headers ) { char conditions[7][16] = { "received", "from", "subject", "date", "content", "boundary" }; int hitcount = 0; int condition_item; char *lc_headers = NULL; if (headers == NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG: Headers are NULL"); return 0; } DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG:----\n%s\n----",FL,headers); lc_headers = strdup(headers); if (lc_headers == NULL) return 0; PLD_strlower(lc_headers); DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG:----(lowercase)----\n%s\n----",FL,lc_headers); for (condition_item=0; condition_item < 6; condition_item++) { char *p; DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG: Condition test item[%d] = '%s'",FL,condition_item,conditions[condition_item]); p = strstr(lc_headers, conditions[condition_item]); if (p != NULL) { if (p > lc_headers) { if ((*(p-1) == '\n')||(*(p-1) == '\r')) hitcount++; } else if (p == lc_headers) hitcount++; } } if (lc_headers != NULL) free(lc_headers); return hitcount; } /*-----------------------------------------------------------------\ Function Name : MIMEH_save_doubleCR Returns Type : int ----Parameter List 1. FFGET_FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_save_doubleCR( FFGET_FILE *f ) { //char c; int c; FILE *fo; struct stat st; // Determine a file name we can use. do { glb.doubleCR_count++; snprintf(glb.doubleCRname,_MIMEH_STRLEN_MAX,"%s/doubleCR.%d",glb.output_dir,glb.doubleCR_count); } while (stat(glb.doubleCRname, &st) == 0); fo = fopen(glb.doubleCRname,"w"); if (!fo) { LOGGER_log("%s:%d:MIMEH_save_doubleCR:ERROR: unable to open '%s' to write (%s)", FL,glb.doubleCRname,strerror(errno)); return -1; } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_save_doubleCR:DEBUG: Saving DoubleCR header: %s\n", FL,glb.doubleCRname); while (1) { c = FFGET_fgetc(f); fprintf(fo,"%c",c); if ((c == EOF)||(c == '\n')) { break; } } fclose(fo); return 0; } /*-----------------------------------------------------------------\ Function Name : * Returns Type : char ----Parameter List 1. MIMEH_absorb_whitespace( char *p , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char * MIMEH_absorb_whitespace( char *p ) { if (p) { while ((*p != '\0')&&((*p == ' ')||(*p == '\t'))) p++; } return p; } /*-----------------------------------------------------------------\ Function Name : MIMEH_strip_comments Returns Type : int ----Parameter List 1. char *input , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Removes comments from RFC[2]822 headers -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_strip_comments( char *input ) { char *p,*p_org; int in_quote=0; if (input == NULL) return 0; p = p_org = input; do { char *q = NULL; // Locate (if any) the first occurance of the ( while ((p_org != NULL)&&((*p_org != '(')||(in_quote==1))) { switch (*p_org) { case '"': in_quote ^= 1; break; case '\n': case '\r': in_quote = 0; break; case '\0': p_org = NULL; break; } if (p_org) p_org++; } p = p_org; if ((p != NULL)&&(in_quote == 0)) { int stop_searching = 0; DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Located open ( at %s",FL,p); // If we did locate an opening parenthesis, look for the closing one // NOTE - we cannot have a breaking \n or \r inbetween // q = strpbrk(p, ")\n\r"); q = p; while ( (q != NULL) && (stop_searching == 0) ) { switch (*q) { case '\0': stop_searching = 1; q = NULL; break; case '\n': case '\r': stop_searching = 1; in_quote = 0; break; case '"': in_quote ^= 1; break; case ')': DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Located closing ) at %s",FL,q); if (in_quote == 0) stop_searching = 1; break; } if ((q != NULL)&&(stop_searching == 0)) q++; } // If we've got both opening and closing, then we need to remove // the contents of the comment, including the parenthesis if (q != NULL) { if (*q != ')') { // if we found a \n or \r between the two (), then jump out // and move p to the next position. p_org++; continue; } else { // Move q to the first char after the closing parenthesis q++; DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: located closing ) at %s ",FL, q); // While there's more chars in string, copy them to where // the opening parenthesis is while (*q != '\0') { *p = *q; p++; q++; } // While q != '\0' DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: char copy done",FL); // Terminate the string *p = '\0'; } // if q !=/= ')' } else break; // if q == NULL } // if p == NULL } while ((p != NULL)&&(p_org != NULL)); // do-while more comments to remove DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Final string = '%s'",FL,input); return 0; } /*------------------------------------------------------------------------ Procedure: MIMEH_read_headers ID:1 Purpose: Reads from the stream F until it detects a From line, or a blank line (end of headers) Input: Output: Errors: ------------------------------------------------------------------------*/ int MIMEH_read_headers( FFGET_FILE *f ) { char buffer[_MIMEH_STRLEN_MAX+1]; int totalsize=0; int linesize=0; int totalsize_original=0; int result = 0; int firstline = 1; int search_count=0; char *tmp; char *tmp_original; char *fget_result = NULL; char *headerline_end; char *p; char *linestart; char *lineend; int is_RFC822_headers=0; // 20040208-1335:PLD: Added to give an indication if the headers are RFC822 like; used in conjunction with the header_longsearch for pulling apart qmail bouncebacks. // Lets start the ugly big fat DO loop here so that we can, if needed // search until we find headers which are actually valid. Personally // I hate this - but people want it. do { search_count++; glb.headerline = NULL; glb.headerline_original = NULL; tmp_original = NULL; while ((fget_result=FFGET_fgets(buffer,_MIMEH_STRLEN_MAX, f))) { linestart = buffer; linesize = strlen(linestart); lineend = linestart +linesize; if (MIMEH_DNORMAL)LOGGER_log("%s:%d:MIMEH_read_headers: Data In=[sz=%d:tb=%d:mem=%p]'%s'",FL, linesize, f->trueblank, glb.headerline, buffer); // If we are being told to copy the input data to an output file // then do so here (this is for the originals) if ((glb.original_header_save_to_file > 0)&&(glb.original_header_file != NULL)) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: saving to file...",FL); fprintf(glb.original_header_file,"%s",linestart); } // if we are being told to keep a copy of the original data // as it comes in from ffget, then do the storage here if (glb.save_headers_original > 0) { if (MIMEH_DNORMAL) LOGGER_log("MIMEH_read_headers:DEBUG:Data-In:[%d:%d] '%s'", strlen(linestart), linesize, linestart); tmp_original = realloc(glb.headerline_original, totalsize_original+linesize+1); if (tmp_original == NULL) { LOGGER_log("%s:%d:MIMEH_read_headers:ERROR: Cannot allocate %d bytes to contain new headers_original ", FL,totalsize_original +linesize +1); if (glb.headerline_original != NULL) free(glb.headerline_original); glb.headerline_original = NULL; return -1; } if (glb.headerline_original == NULL) { glb.headerline_original = tmp_original; totalsize_original = linesize +1; PLD_strncpy( glb.headerline_original, linestart, (linesize+1)); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: '%s'", FL, glb.headerline_original); } else { glb.headerline_original = tmp_original; PLD_strncpy( (glb.headerline_original +totalsize_original -1), linestart, (linesize +1)); totalsize_original += linesize; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: HO = '%s'", FL, glb.headerline_original); } //LOGGER_log("DEBUG:linesize=%d data='%s'",linesize, linestart); } /** Normal processing of the headers now starts. **/ if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: realloc'ing dataspace",FL); tmp = realloc(glb.headerline, totalsize+linesize+1); if (tmp == NULL) { LOGGER_log("%s:%d:MIMEH_read_headers:ERROR: Cannot allocate %d bytes to contain new headers ", FL,totalsize +linesize +1); if (glb.headerline != NULL) free(glb.headerline); glb.headerline = NULL; return -1; } if (glb.headerline == NULL) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Initial appending of head to dataspace headerline = NULL realloc block = %p linestart = %p linesize = %d",FL, tmp, linestart, linesize); glb.headerline = tmp; totalsize = linesize; PLD_strncpy(glb.headerline, linestart, (linesize +1)); headerline_end = glb.headerline +totalsize; } // If the global headerline is currently NULL else { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Appending of new data to existing header existing-headerline = %p new realloc block = %p linestart = %p linesize = %d",FL, glb.headerline, tmp, linestart, linesize); // Perform header unfolding by removing any CRLF's // of the last line if the first characters of the // newline are blank/space glb.headerline = tmp; if ((linestart < lineend)&&((*linestart == '\t')||(*linestart == ' '))) { // Here we start at the last character of the previous line // and check to see if it's a 'space' type charcter, if it is // we will then reduce the total size of the headers thus far and // move the pointer where we're going to append this new line back // one more character - Ultimately what we wish to achieve is that // the new line will tacked on [sans leading spaces] to the end of // the previous line. // // 'p' holds the location at the -end- of the current headers where // we are going to append the newly read line p = glb.headerline +totalsize -1; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: unwrapping headers headers=%p, p = %p",FL,glb.headerline, p); while ((p >= glb.headerline)&&(( *p == '\n' )||( *p == '\r' ))) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Removing trailing space p=[%p]%c",FL, p, *p); *p = '\0'; p--; totalsize--; } p = glb.headerline +totalsize -1; } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Memcopying line, source = %p, dest = %p, size = %d", FL, linestart, glb.headerline +totalsize, linesize); memcpy((glb.headerline +totalsize), linestart, (linesize)); totalsize += linesize; *(glb.headerline +totalsize) = '\0'; } // If the glb.headerline already is allocated and we're appending to it. if (f->trueblank) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Trueblank line detected in header reading",FL); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Headers /before/ decoding\n-------\n%s\n-------------------",FL, glb.headerline); MDECODE_decode_ISO( glb.headerline, totalsize ); if ((glb.save_headers)&&(glb.headerline)) { fprintf(glb.header_file,"%s",glb.headerline); } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Final Headers\n------------------\n%s---------------", FL,glb.headerline); //result = 1; //result = 0; break; } // If the last line was in fact a true blank line // If there was a doubleCR at the end of the line, // then we need to save the next set of data until there // is a \n if (FFGET_doubleCR) { if (glb.doubleCR_save != 0) { MIMEH_save_doubleCR(f); glb.doubleCR = 1; } FFGET_doubleCR = 0; FFGET_SDL_MODE = 0; } // FFGET_doubleCR test firstline = 0; } // While reading more headers from the source file. // If FFGET ran out of data whilst processing the headers, then acknowledge this // by returning a -1. // // NOTE - This does not mean we do not have any data! // it just means that our input ran out. if (!fget_result) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:ERROR: FFGET module ran out of input while reading headers",FL); result = -1; } else { if (glb.header_longsearch > 0) { /** Test the headers for RFC compliance... **/ is_RFC822_headers = MIMEH_are_headers_RFC822(glb.headerline); if (is_RFC822_headers == 0) { /** If not RFC822 headers, then clean up everything we allocated in here **/ DMIMEH LOGGER_log("%s:%d:MIME_read_headers:DEBUG: No RFC822 headers detected, cleanup."); MIMEH_headers_cleanup(); } } } } while ((is_RFC822_headers==0)&&(glb.header_longsearch>0)&&(result==0)&&(search_countcontent_type\ ,hinfo->boundary\ ,hinfo->filename\ ,hinfo->name\ ,hinfo->content_transfer_encoding\ ,hinfo->content_disposition); fflush(stdout); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_decode_multivalue_language_string Returns Type : int ----Parameter List 1. char *input , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_decode_multivalue_language_string( char *input ) { int sq_count = 0; int language_set = 0; char *q = input; DMIMEH LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:DEBUG: Decoding '%s'",FL,input); // Count the single-quotes while ((*q != '\0')&&(sq_count != 2)) if (*q++ == '\'') sq_count++; if (sq_count < 2) { // LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:WARNING: Insufficient single quotes for valid language-charset string",FL); q = input; } else { language_set = 1; } // q will be pointing at the 2nd single-quote, which is the end of // the language encoding set, so we just jump over that and start // reading off the data and decoding it. MDECODE_decode_multipart( q ); // If the language was set, we need to move down our decoded data to the // start of the input buffer if (language_set == 1) { while (*q != '\0') { *input = *q; input++; q++; } *input = '\0'; } DMIMEH LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:DEBUG: Output = '%s'",FL,q); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_recompose_multivalue Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, Global header information, can be NULL 2. char *header_name_prefix, Prefix we're looking for (ie, filename) 3. char *header_value, String which the prefix should exist in 4. char *buffer, Output buffer 5. size_t buffer_size , Output buffer size ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Multivalue strings are ones which appear like: filename*0*=us-ascii'en-us'attachment%2E%65 filename*1*="xe" which should duly be recoded as: filename=attachment.exe Another example: (extracted from the RFC2231 document) Content-Type: application/x-stuff title*0*=us-ascii'en'This%20is%20even%20more%20 title*1*=%2A%2A%2Afun%2A%2A%2A%20 title*2="isn't it!" -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_recompose_multivalue( struct MIMEH_header_info *hinfo, char *header_name_prefix, char *header_value, char *buffer, size_t buffer_size, char **data_end_point ) { int result = 0; char *start_position = header_value; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: seeking for %s in %s and appending to '%s'. Buffer size=%d", FL, header_name_prefix, header_value,buffer, buffer_size ); // Locate the first part of the multipart string start_position = strstr(header_value, header_name_prefix); if (start_position != NULL) { char *q; char *buffer_start; // Setup our buffer insertion point for what ever new data we extract buffer_start = buffer +strlen(buffer); buffer_size -= strlen(buffer); q = start_position; // If the string we're looking for exists, then continue... do { char *p; char *end_point; char end_point_char='\0'; int decode_data=0; int q_len; p = strstr(q, header_name_prefix); if (p == NULL) break; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: prefix = %s", FL, p); q = strchr(p,'='); if (q == NULL) break; // Test to see if we have to look for a language encoding specification *sigh* if (*(q-1) == '*') { decode_data=1; } // Move the pointer past the '=' separator q++; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: data = %s", FL, q); // Find where this multipart string ends end_point = strpbrk(q, ";\t\n\r "); if (end_point != NULL) { *end_point = '\0'; end_point_char = *end_point; *data_end_point = end_point; // Set this so we know where to start decoding the next time we call this fn } else { char *ep; // If strpbrk comes up with nothing, then we set the data_end_point to the end of the string ep = q; while (*ep != '\0') ep++; *data_end_point = ep; } // Trim off quotes. if (*q == '"') { int bl; // LOGGER_log("%s:%d:DEBUG: Trimming '%s'", FL, q); q++; bl = strlen(q); if (*(q +bl -1) == '"') *(q +bl -1) = '\0'; //LOGGER_log("%s:%d:DEBUG: Trim done, '%s'", FL, q); } if (decode_data == 1) { MIMEH_decode_multivalue_language_string(q); } DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: segment value = '%s', appending to '%s'", FL, q, buffer); snprintf(buffer_start,buffer_size,"%s",q); q_len = strlen(q); buffer_size -= q_len; buffer_start += q_len; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: Buffer[remaining=%d]= '%s'", FL, buffer_size,buffer); if (end_point != NULL) { *end_point = end_point_char; q = end_point +1; } else q = NULL; } while ((q != NULL)&&(buffer_size > 0)); } DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: End point set to: [%d] '%s'",FL, (*data_end_point -header_value), *data_end_point); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_header_parameter Returns Type : int ----Parameter List 1. char *data, 2. char *searchstr, 3. char *output_value, 4. int output_value_size , 5. char *data_end_point, used to keep track of the last point of successful data decoding is. ------------------ Exit Codes : 0 = Success, found the required parameter 1 = No luck, didn't find the required parameter Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 11-Aug-2004: Added new variable, data_end_point. This variable was required because without it, there was no way of telling where to continue on the search for more valid data, this is due to having to no longer rely on fixed atom separators like : and ; in the MIME text (thankyou MUA's which incorrectly interpreted the RFC's *sigh*) \------------------------------------------------------------------*/ int MIMEH_parse_header_parameter( struct MIMEH_header_info *hinfo, char *data, char *searchstr, char *output_value, int output_value_size, char **data_end_point ) { int return_value = 0; char *p; char *hl; // Set the data end point to be the beginning of the data, as we // have not yet searched through any of the header data *data_end_point = data; // Duplicate and convert to lowercase the header data // that we have been provided with. hl = strdup(data); PLD_strlower(hl); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Seeking '%s' in '%s'", FL, searchstr, hl); // Look for the search string we're after (ie, filename, name, location etc) if (strncmp(hl,searchstr,strlen(searchstr))==0) p = hl; else p = NULL; // p = strstr (hl, searchstr); //TESTING if (p != NULL) { char *string = NULL; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: found %s in %s", FL, searchstr, p); // Work out where in the -original- string the located parameter is. // We need to work from the original string because we need to // preserve case and our searching string is in _lower-case_. // // After we've located it, we offset the pointer past the string we // searched for. At this position, we should see a separator of // some type in the set [*;:=\t ]. string = p -hl +data +strlen(searchstr); /** ** After searching for our parameter, if we've got a ** basic match via strstr, we should then proceed to ** check that the characters either side of it are ** relevant to a typical parameter specification ** ** the characters *, =, and tab can succeed a ** parameter name. **/ switch (*string) { case '*': case '=': case ' ': case '\t': /** ** Permitted characters were found after the parameter name ** so continue on... **/ break; default: /** ** Something other than the permitted characters was found, ** this implies (assumed) that the string match was actually ** just a bit of good luck, return to caller **/ if (hl) free(hl); return 1; } /** Switch **/ /** ** Don't forget to also test the character _BEFORE_ the search string **/ if (1) { char *before_string; before_string = string -1 -strlen(searchstr); if (before_string >= data) { /** ** The characters, , , ;, : may preceed a parameter name **/ switch (*(before_string)) { case ';': case ':': case ' ': case '\t': /** ** Permitted characters were found after the parameter name ** so continue on... **/ break; default: /** ** Something other than the permitted characters was found, ** this implies (assumed) that the string match was actually ** just a bit of good luck, return to caller **/ if (hl) free(hl); return 1; } /** Switch before_string **/ } /** if before_string > data **/ } /** 1 **/ // If the char is a '*', this means we've got a multivalue parameter // which needs to be decoded (ie, name*1*=foo name*2*=bar ) if (*string == '*') { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Found a '*' after the name, so attempting multipart value decode",FL); // PLD:DEV:11/08/2004-18H30 // Issue: RFC2231 handling return_value = MIMEH_recompose_multivalue( hinfo, searchstr, data, output_value, output_value_size, data_end_point); } else { // skip any spaces while (isspace((int) *string )) string++; //if ( *string != '=' ) if ( *string == '\0' ) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: In '%s' parsing, was expecting a '=' in the start of '%s'\n", FL, searchstr, string ); } else { char *endchar; // Eliminate multiple = separators. // Reference: c030804-006a // PLD:DEV: 11/08/2004-15H15 while ((*(string +1) == '=')&&(*(string+1) != '\0')) { string++; MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS); } // Get the end of our string endchar = string +strlen(string) -1; *data_end_point = endchar; // Strip off trailing whitespace while ((endchar > string)&&(isspace((int)*endchar))) { *endchar = '\0'; endchar--; } // we are at the '=' in the header, so skip it if (*string == '=') string++; else { MIMEH_set_defect(hinfo,MIMEH_DEFECT_MISSING_SEPARATORS); } // skip any spaces... again while ( isspace((int) *string ) ) string++; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Extracting value out of '%s'",FL,string); // Because of all the potential exploits and bad behaviour // we have to be really careful about how we determine // what the enclosed string is for our parameter. // // Previously we could _assume_ that we just get the last // quote (") on the line and copy out what was between, // unfortunately that doesn't work anymore. Instead now // we have to step along the data stream one char at a // time and make decisions along the way. switch (*string) { case '\"': { // If our first char is a quote, then we'll then try and find // the second quote which closes the string, alas, this is // not always present in the header data, either due to a // broken MUA or due to an exploit attempt. char *string_end; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Using quoted-string tests",FL); // Remove multiple-sequential quotes string++; while ((*string != '\0')&&(*string == '\"')){ string++; MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_QUOTES); } // Find the next quote which isn't sequential to the above // quotes that we just skipped over string_end = strchr(string+1, '\"'); if (string_end != NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: End of value found",FL); *string_end = '\0'; *data_end_point = string_end +1; } else { // If string_end == NULL // // If we didn't find any more quotes, that // means we've probably got an unbalanced string (oh joy) // so then we convert to looking for other items such as // ;\n\r\t and space. // if (hinfo) MIMEH_set_defect(hinfo,MIMEH_DEFECT_UNBALANCED_QUOTES); string_end = strpbrk(string,"; \n\r\t"); if (string_end != NULL) { *string_end = '\0'; *data_end_point = string_end +1; } else { // There is no termination to the string, instead the // end of the string is \0. } } } break; default: { char *string_end; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Using NON-quoted-string tests",FL); string_end = strpbrk(string,"; \n\r\t"); if (string_end != NULL) { *string_end = '\0'; *data_end_point = string_end +1; } else { // There is no termination to the string, instead the // end of the string is \0. } } break; } /** end of switch **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Extracting value out of '%s'",FL,string); // Trim up and leading/trailing quotes if (((*string == '\"')&&(*(string +strlen(string)-1) == '\"')) || ((*string == '\'')&&(*(string +strlen(string)-1) == '\'')) ) { int slen = strlen(string) -2; char *s = string; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse-header_parameter:DEBUG: Stripping quotes from '%s'",FL,string); while (slen > 0) { *s = *(s+1); s++; slen--; } *s = '\0'; } // Now that our string is all cleaned up, save it to our output value snprintf( output_value, output_value_size, "%s", string ); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Final value = '%s'",FL, output_value); } // If the first non-whitespace char wasn't a '=' } // If the first char after the search-string wasn't a '*' } else { return_value = 1; } if (hl != NULL) free(hl); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: [return=%d] Done seeking for '%s' data_end_point=%p (from %p)",FL, return_value, searchstr, *data_end_point, data); return return_value; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_valid_header_prefix Returns Type : int ----Parameter List 1. char *data, 2. char *prefix_name , ------------------ Exit Codes : 0 = no, not valid 1 = yes, valid. Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_valid_header_prefix( char *data, char *prefix_name ) { int plen = strlen(prefix_name); /** If our string doesn't start with content-type, then exit **/ if (strncasecmp(data, prefix_name, plen)!=0) { return 0; } else { char end_char; /** Test to see that the terminating char after the content-type ** string is suitable to indicating that the content-type is ** infact a header name **/ end_char = *(data +plen); switch (end_char){ case ':': case ' ': case '\t': case '\0': /** Valid terminating characters found **/ break; default: /** Otherwise, return 0 **/ return 0; } /** switch end_char **/ } /** if-else **/ return 1; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttype_linear Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttype_linear_EXPERIMENT( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *chv = header_value; char *chn = header_name; int boundary_found = 0; // int name_found = 0; // int filename_found = 0; /** Absorb whitespace **/ while (isspace(*chn)) chn++; /** Test if the content-type string is valid **/ if (MIMEH_is_valid_header_prefix(chn, "content-type")==0) return 0; /** Now, let's try parse our content-type parameter/value string **/ while (*chv) { while (isspace(*chv)) chv++; if ((boundary_found==0)&&(MIMEH_is_valid_header_prefix(chv,"boundary")==1)) { } // if (strncasecmp(chv, "boundary" } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttype Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttype( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { int return_value; char *p, *q; char *hv = strdup( header_value ); // CONTENT TYPE ------------------------------- // CONTENT TYPE ------------------------------- // CONTENT TYPE ------------------------------- DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Start",FL); p = strstr(header_name,"content-type"); if (p != NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype: Content-type string found in header-name",FL); /** 20041216-1106:PLD: Increase our sanity **/ hinfo->sanity++; PLD_strlower( header_value ); q = header_value; if (strstr(q,"multipart/appledouble")) hinfo->content_type = _CTYPE_MULTIPART_APPLEDOUBLE; else if (strstr(q,"multipart/signed")) hinfo->content_type = _CTYPE_MULTIPART_SIGNED; else if (strstr(q,"multipart/related")) hinfo->content_type = _CTYPE_MULTIPART_RELATED; else if (strstr(q,"multipart/mixed")) hinfo->content_type = _CTYPE_MULTIPART_MIXED; else if (strstr(q,"multipart/alternative")) hinfo->content_type = _CTYPE_MULTIPART_ALTERNATIVE; else if (strstr(q,"multipart/report")) hinfo->content_type = _CTYPE_MULTIPART_REPORT; else if (strstr(q,"multipart/")) hinfo->content_type = _CTYPE_MULTIPART; else if (strstr(q,"text/plain")) hinfo->content_type = _CTYPE_TEXT_PLAIN; else if (strstr(q,"text/html")) hinfo->content_type = _CTYPE_TEXT_HTML; else if (strstr(q,"text/")) hinfo->content_type = _CTYPE_TEXT; else if (strstr(q,"image/gif")) hinfo->content_type = _CTYPE_IMAGE_GIF; else if (strstr(q,"image/jpeg")) hinfo->content_type = _CTYPE_IMAGE_JPEG; else if (strstr(q,"image/")) hinfo->content_type = _CTYPE_IMAGE; else if (strstr(q,"audio/")) hinfo->content_type = _CTYPE_AUDIO; else if (strstr(q,"message/rfc822")) hinfo->content_type = _CTYPE_RFC822; else if (strstr(q,"/octet-stream")) hinfo->content_type = _CTYPE_OCTECT; else if (strstr(q,"/ms-tnef")) hinfo->content_type = _CTYPE_TNEF; else if (strstr(q,"application/applefile")) { hinfo->content_type = _CTYPE_APPLICATION_APPLEFILE; if ( hinfo->filename[0] == '\0' ) { if (strlen(glb.appledouble_filename)>0) { snprintf(hinfo->filename, sizeof(hinfo->filename), "%s.applemeta", glb.appledouble_filename ); } else { snprintf(hinfo->filename, sizeof(hinfo->filename), "applefile"); } } } else hinfo->content_type = _CTYPE_UNKNOWN; /** Is there an x-mac-type|creator parameter? **/ if ((strstr(header_value,"x-mac-type="))&&(strstr(header_value,"x-mac-creator="))) { /** By setting this flag to 1, we are saying that if the ** filename contains a forward slash '/' char, then it's ** to be treated as a normal char, not a directory ** separator. However, as we cannot generate a filename ** with that char normally, we'll convert it to something ** else **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Located x-mac attachment",FL); hinfo->x_mac = 1; FNFILTER_set_mac(hinfo->x_mac); } // Copy the string to our content-type string storage field p = header_value; if (p != NULL) { char *c = p; // Step 1 - jump over any whitespace while ( *c == ' ' || *c == '\t') c++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_type_string, c, _MIMEH_CONTENT_TYPE_MAX); // Step 3 - clean up the string c = hinfo->content_type_string; while (*c && *c != ' ' && *c != '\t' && *c != '\n' && *c != '\r' && *c != ';') c++; // Step 4 - Terminate the string *c = '\0'; } // If we have an additional parameter at the end of our content-type, then we // should search for a name="foobar" sequence. //p = strchr( hv, ';' ); p = strpbrk( hv, ";\t\n\r " ); if (p != NULL) { char *param = NULL; char *data_end_point = param; p++; param = strpbrk( p, ";\n\r\t " ); while ( param != NULL ) { /** ** ** The Process of decoding our line.... ** . While not end of the line... ** . Remove whitespace ** . test for 'name' ** . test for 'boundary' ** . Move to next char after parameter values ** ** Go to the next character after the 'token separator' character ** and then proceed to absorb any excess white space. ** Once we've stopped at a new, non-white character, we can begin ** to see if we've got a sensible parameter like name=, filename= ** or boundary= **/ param++; param = MIMEH_absorb_whitespace(param); /** ** If we get to the end of the line, just break out of the token ** parsing while loop **/ if (*param == '\0') break; /** ** Look for name or filename specifications in the headers ** Look for name or filename specifications in the headers ** Look for name or filename specifications in the headers **/ return_value = MIMEH_parse_header_parameter( hinfo, param, "name", hinfo->name, sizeof(hinfo->name), &data_end_point); /** Update param to point where data_end_point is ** this is so when we come around here again due ** to the while loop, we'll know where to pick up ** the search for more parameters **/ if (data_end_point > param) param = data_end_point; // If we finally had success, then copy the name into filename for hinfo if ( return_value == 0 ) { // Move the parameter search point up to where we stopped // processing the data in the MIMEH_parse_header_parameter() call DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushing new filename to stack '%s'",FL, hinfo->name); /** Step 1: Check to see if this filename already ** exists in the stack. We do this so that we don't ** duplicate entries and also to prevent false ** bad-header reports. **/ if (SS_cmp(&(hinfo->ss_names), hinfo->name, strlen(hinfo->name))==NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Filtering '%s'",FL, hinfo->name); FNFILTER_filter(hinfo->name, _MIMEH_FILENAMELEN_MAX); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushing '%s'",FL, hinfo->name); SS_push(&(hinfo->ss_names),hinfo->name,strlen(hinfo->name)); if (SS_count(&(hinfo->ss_names)) > 1) { MIMEH_set_defect(hinfo, MIMEH_DEFECT_MULTIPLE_NAMES); } if ( hinfo->filename[0] == '\0' ) { snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", hinfo->name ); } } /* If the file name doesn't already exist in the stack */ } /* If a filename was located in the headers */ /** ** Look for the MIME Boundary specification in the headers ** Look for the MIME Boundary specification in the headers ** Look for the MIME Boundary specification in the headers **/ return_value = MIMEH_parse_header_parameter(hinfo, param, "boundary", hinfo->boundary, sizeof(hinfo->boundary), &data_end_point); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Param<=>data_end gap = %d", FL,data_end_point -param); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); if (data_end_point > param) param = data_end_point; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); if ( return_value == 0 ) { // Move the parameter search point up to where we stopped // processing the data in the MIMEH_parse_header_parameter() call //hinfo->boundary_located = 1; hinfo->boundary_located++; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushed boundary to stack (%s)",FL, hinfo->boundary); BS_push(hinfo->boundary); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Setting hinfo->boundary_located to %d",FL, hinfo->boundary_located ); if (hinfo->boundary_located > 1) { // Register the defect MIMEH_set_defect(hinfo, MIMEH_DEFECT_MULTIPLE_BOUNDARIES); //Reset the counter back to 1. hinfo->boundary_located=1; } } //param = PLD_strtok( &tx, NULL, ";\n\r" ); // * PLD:20040831-22H15: Added 'if (param != NULL)' prefix to debugging lines // * In response to bug #32, submitted by ICL ZA if (param != NULL) DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); param = strpbrk( param, ";\n\r " ); if (param != NULL) DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); } // While } } if (hv != NULL) free(hv); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: end.",FL); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contentlocation Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contentlocation( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p, *q; // CONTENT LOCATION ------------------------------- // CONTENT LOCATION ------------------------------- // CONTENT LOCATION ------------------------------- PLD_strlower( header_name ); p = strstr(header_name,"content-location"); if (p) { /** 20041216-1108:PLD: Increase our sanity **/ hinfo->sanity++; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_contentlocation:DEBUG: Content Location line found - '%s'\n", FL, header_value); p = q = header_value; while (q) { q = strpbrk(p, "\\/"); if (q != NULL) p = q+1; } if (p) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_contentlocation:DEBUG: filename = %s\n", FL, p); snprintf(hinfo->name, sizeof(hinfo->name),"%s",p); snprintf(hinfo->filename, sizeof(hinfo->name),"%s",p); FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX); SS_push(&(hinfo->ss_filenames), hinfo->filename, strlen(hinfo->filename)); } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttransferencoding Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttransferencoding( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p, *q; char c = '\n'; // CONTENT TRANSFER ENCODING --------------------- // CONTENT TRANSFER ENCODING --------------------- // CONTENT TRANSFER ENCODING --------------------- p = strstr(header_name,"content-transfer-encoding"); if (p) { /** 20041216-1107:PLD: Increase our sanity **/ hinfo->sanity++; q = strpbrk(header_value,"\n\r;"); if (q != NULL) { c = *q; *q = '\0'; } p = header_value; PLD_strlower( p ); if (strstr(p,"base64")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_B64; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to BASE64", FL); } else if (strstr(p,"7bit")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_7BIT; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to 7-BIT ", FL); } else if (strstr(p,"8bit")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_8BIT; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to 8-BIT", FL); } else if (strstr(p,"quoted-printable")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_QP; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to Quoted-Printable", FL); } else if (strstr(p,"binary")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_BINARY; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to Binary", FL); } else if ( (strstr(p,"uu")) ||(strstr(p,"x-u")) ||(strcmp(p,"u") == 0) ) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_UUENCODE; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to UUENCODE", FL); } else hinfo->content_transfer_encoding = _CTRANS_ENCODING_RAW; // Copy the string to our content-transfer string storage field p = header_value; if (p != NULL) { char *cp = p; // Step 1 - jump over any whitespace while ( *cp == ' ' || *cp == '\t') cp++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_transfer_encoding_string, cp, _MIMEH_CONTENT_TRANSFER_ENCODING_MAX); // Step 3 - clean up the string cp = hinfo->content_transfer_encoding_string; while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\r' && *cp != ';') cp++; // Step 4 - Terminate the string *cp = '\0'; } // Set the character which we changed to a \0 back to its original form so that // we don't cause problems from tainted data for any further parsing calls // which use the data. if (q != NULL) *q = c; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contentdisposition Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contentdisposition( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p; char *hv = strdup(header_value); // CONTENT DISPOSITION ------------------------------ // CONTENT DISPOSITION ------------------------------ // CONTENT DISPOSITION ------------------------------ //LOGGER_log("%s:%d:DEBUG: Headers='%s'",FL,header_value); p = strstr(header_name,"content-disposition"); if (p != NULL) { /** 20041216-1107:PLD: Increase our sanity **/ hinfo->sanity++; // Change p to now point to the header VALUE, p no longer // points to the content-disposition start! p = header_value; PLD_strlower( header_value ); // Here we just check to find out what type of disposition we have. if (strstr(p,"inline")) { hinfo->content_disposition = _CDISPOSITION_INLINE; } else if (strstr(p,"form-data")) { hinfo->content_disposition = _CDISPOSITION_FORMDATA; } else if (strstr(p,"attachment")) { hinfo->content_disposition = _CDISPOSITION_ATTACHMENT; } else { hinfo->content_disposition = _CDISPOSITION_UNKNOWN; } // Copy the string to our content-transfer string storage field if (p != NULL) { char *q = p; // Step 1 - jump over any whitespace while ( *q == ' ' || *q == '\t') q++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_disposition_string, q, _MIMEH_CONTENT_DISPOSITION_MAX); // Step 3 - clean up the string q = hinfo->content_disposition_string; while (*q && *q != ' ' && *q != '\t' && *q != '\n' && *q != '\r' && *q != ';') q++; // Step 4 - Terminate the string *q = '\0'; } DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Disposition string = '%s'",FL, hv); // Commence to decode the disposition string into its components. p = strpbrk( hv, ";\t\n\r " ); if (p != NULL) { // struct PLD_strtok tx; char *param; hinfo->name[0]='\0'; p++; param = p; while ( param != NULL ) { int parse_result; char *data_end_point; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Parsing '%s'",FL,param); // Seek out possible 'filename' parameters parse_result = MIMEH_parse_header_parameter(hinfo, param, "filename", hinfo->name, sizeof(hinfo->name), &data_end_point); if (data_end_point > param) param = data_end_point; if (parse_result == 0) { FNFILTER_filter(hinfo->name, _MIMEH_FILENAMELEN_MAX); SS_push(&(hinfo->ss_filenames), hinfo->name, strlen(hinfo->name)); if (SS_count(&(hinfo->ss_filenames)) > 1) { MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_FILENAMES); } } param = strpbrk( param , ";\n\r\t " ); if (param) param++; //param = PLD_strtok( &tx, NULL, ";\n\r\t " ); } // While if ( hinfo->filename[0] == '\0' ) { snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", hinfo->name ); } // Handle situations where we'll need the filename for the future. if ( hinfo->content_type == _CTYPE_MULTIPART_APPLEDOUBLE ) { snprintf( glb.appledouble_filename, sizeof(glb.appledouble_filename), "%s", hinfo->filename ); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Setting appledouble filename to: '%s'",FL,glb.appledouble_filename); } } // If the header-value contained ;'s ( indicating parameters ) } // If the header-name actually contained 'content-disposition' if (hv != NULL) free(hv); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_subject Returns Type : int ----Parameter List 1. char *header_name, contains the full headers 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_generic( char *header_name, char *header_value, struct MIMEH_header_info *hinfo, char *tokenstr, char *buffer, size_t bsize ) { int compare_result = 0; int tlen; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_generic:DEBUG: Searching for %s in %s",FL,tokenstr,header_name); /** Sanity check the parameters **/ if (hinfo == NULL) return -1; if (tokenstr == NULL) return -1; if (header_name == NULL) return -1; if (header_value == NULL) return -1; if (buffer == NULL) return -1; if (bsize < 1) return -1; tlen = strlen(tokenstr); compare_result = strncmp( header_name, tokenstr, tlen ); if (compare_result == 0) { switch (*(header_name +tlen)) { case ':': case ' ': case '\t': case '\0': DMIMEH LOGGER_log("%s:%d:MIMEH_parse_generic:DEBUG: Located! Sanity up +1",FL); snprintf( buffer, bsize, "%s", header_value ); hinfo->sanity++; break; } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_subject Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_subject( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { int result = 0; result = MIMEH_parse_generic( header_name, header_value, hinfo, "subject", hinfo->subject, sizeof(hinfo->subject) ); snprintf(glb.subject, sizeof(glb.subject),"%s", hinfo->subject); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_date Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_date( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "date", hinfo->date, sizeof(hinfo->date) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_from Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_from( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "from", hinfo->from, sizeof(hinfo->from) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_to Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_to( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "to", hinfo->to, sizeof(hinfo->to) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_messageid Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_messageid( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "message-id", hinfo->messageid, sizeof(hinfo->messageid) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_received Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_received( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "received", hinfo->received, sizeof(hinfo->received) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_process_headers Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. char *headers , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_process( struct MIMEH_header_info *hinfo, char *headers ) { /** scan through our headers string looking for information that is ** valid **/ char *safeh, *h, *safehl; char *current_header_position; int headerlength; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Start [hinfo=%p]\n",FL, hinfo); safeh = h = headers; /** Duplicate the headers for processing - this way we don't 'taint' the ** original headers during our searching / altering. **/ headerlength = strlen(h); safehl = malloc(sizeof(char) *(headerlength+1)); PLD_strncpy(safehl, h, headerlength+1); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_headers:DEBUG: Header length = %d\n", FL,headerlength); MIMEH_strip_comments(h); current_header_position = h; // Searching through the headers, we seek out header 'name:value;value;value' sets, // Each set is then cleaned up, seperated and parsed. while ((current_header_position != NULL)&&( current_header_position <= (h +headerlength) )) { char *header_name, *header_value; char *header_name_end_position; char *header_value_end_position; DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Processing '%s'",FL,current_header_position); /** Tokenise for the header 'name', ie, content-type, subject etc **/ header_name = current_header_position; header_name_end_position = strpbrk( header_name, ":\t " ); if (header_name_end_position == NULL) { // We couldn't find a terminating :, so, instead we try to find // the first whitespace // // PLD:DEV:11/08/2004-15H27 // Issue: c030804-006a // // NOTE: this may activate on the true-blank lines, hence why we // dump the source string, just for confirmation DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Could not locate ':' separator, using whitespace (source='%s')",FL,header_name); header_name_end_position = strpbrk( header_name, "\t " ); if (header_name_end_position == NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Cannot find a header name:value pair in '%s'",FL, header_name); } } // Seek forward from the start of our header, looking for the first occurance // of the line end (implying the end of the current header name:value, // we can do this because we know that when the headers were read in, we // have already unfolded them, such that there should only be one header:value // pairing per 'line'. current_header_position = strpbrk( current_header_position, "\n\r"); if ( current_header_position == NULL ) { // Theoretically, this should not happen, as headers are always // terminated with a \n\r\n\r finishing byte sequence, thus // if this _does_ happen, then we will simply jump out of the // current iteration and let the loop try find another pairing // // There probably should be a logging entry here to indicate that // "something strange happened" continue; } else { // Shuffle our CHP (current-header-position) pointer along the header // data until it is no longer pointing to a \r or \n, this is so // that when the next iteration of this loop comes around, it'll // immediately be in the right place for starting the next parse while (( *current_header_position == '\n') ||( *current_header_position == '\r' )) current_header_position++; } if (( header_name_end_position == NULL )||( header_name_end_position > current_header_position)) { // Some headers can contain various levels of non name/value pairings, // while their presence could be debatable in terms of RFC validity // we will 'ignore' them rather than throwing up our arms. This // ensures that we are not made to break over spurilous data. if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: This line contains no header:value pair (%s)", FL, current_header_position); continue; } else { // Get the header-value string and prepare to // parse the data through our various parsing // functions. header_value = header_name_end_position +1; header_value_end_position = strpbrk( header_value, "\n\r" ); if ( header_value_end_position != NULL ) { *header_name_end_position = '\0'; *header_value_end_position = '\0'; if (MIMEH_DNORMAL) { LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Header Name ='%s'", FL, header_name ); LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Header Value='%s'", FL, header_value ); } // To make parsing simpler, convert our // header name to lowercase, that way // we also reduce the CPU requirements for // searching because pre-lowering the header-name // occurs once, but string testing against it // occurs multiple times ( at least once per parsing PLD_strlower( header_name ); MIMEH_parse_subject( header_name, header_value, hinfo ); MIMEH_parse_contenttype( header_name, header_value, hinfo ); MIMEH_parse_contenttransferencoding( header_name, header_value, hinfo ); MIMEH_parse_contentdisposition( header_name, header_value, hinfo ); /** These items aren't really -imperative- to have, but they do ** help with the sanity checking **/ MIMEH_parse_date( header_name, header_value, hinfo ); MIMEH_parse_from( header_name, header_value, hinfo ); MIMEH_parse_to( header_name, header_value, hinfo ); MIMEH_parse_messageid( header_name, header_value, hinfo ); MIMEH_parse_received( header_name, header_value, hinfo ); if (hinfo->filename[0] == '\0') { MIMEH_parse_contentlocation( header_name, header_value, hinfo ); } } else { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headerss:DEBUG: Header value end position is NULL",FL); } } } // while // Final analysis on our headers: if ( hinfo->content_type == _CTYPE_MULTIPART_APPLEDOUBLE ) { char tmp[128]; snprintf( tmp, sizeof(tmp), "mac-%s", hinfo->filename ); snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", tmp ); snprintf( hinfo->name, sizeof(hinfo->name), "%s", tmp ); } // PLD:20031205 // Act like Outlook *God forbid!* and if there's a octect-stream // content-type, but the encoding is still null/empty, then // change the content-encoding to be RAW if ( hinfo->content_type == _CTYPE_OCTECT ) { if ((hinfo->content_transfer_encoding == _CTRANS_ENCODING_UNSPECIFIED) || (hinfo->content_transfer_encoding == _CTRANS_ENCODING_UNKNOWN) || (strlen(hinfo->content_transfer_encoding_string) < 1) ) { //LOGGER_log("%s:%d:DEBUG: Encoding pair was octet but no encoding, filename=%s\n",FL,hinfo->filename); hinfo->content_transfer_encoding = _CTRANS_ENCODING_RAW; } } if (safehl) { free(safehl); } else LOGGER_log("%s:%d:MIME_parse_headers:WARNING: Unable to free HEADERS allocated memory\n", FL); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: END [hinfo=%p]\n", FL, hinfo); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. FFGET_FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_get( struct MIMEH_header_info *hinfo, FFGET_FILE *f ) { int result = 0; // Setup some basic defaults hinfo->filename[0] = '\0'; hinfo->name[0] = '\0'; hinfo->content_type = _CTYPE_UNKNOWN; hinfo->subject[0] = '\0'; // 20040116-1234:PLD - added to appease valgrind hinfo->content_disposition = 0; hinfo->content_transfer_encoding = 0; hinfo->boundary_located = 0; // Initialise header defects array. hinfo->header_defect_count = 0; memset(hinfo->defects, 0, _MIMEH_DEFECT_ARRAY_SIZE); snprintf( hinfo->content_type_string, _MIMEH_CONTENT_TYPE_MAX , "text/plain" ); // Read from the file, the headers we need FFGET_set_watch_SDL(1); result = MIMEH_read_headers(f); FFGET_set_watch_SDL(0); // If we ran out of input whilst looking at headers, then, we basically // flag this, free up the headers, and return. if (result == -1) { if (glb.headerline) free(glb.headerline); return result; } // If we came back with an OKAY result, but there's nothing in the // headers, then flag off an error if (glb.headerline == NULL) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_headers:DEBUG: null headerline\n", FL); return 1; } return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_headers_cleanup Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_cleanup( void ) { if (glb.headerline != NULL) { free(glb.headerline); glb.headerline = NULL; } if (glb.headerline_original != NULL) { free(glb.headerline_original); glb.headerline_original = NULL; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_headers Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_headers( FFGET_FILE *f, struct MIMEH_header_info *hinfo ) { int result = 0; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Start [F=%p, hinfo=%p]\n", FL, f, hinfo); /** 20041216-1100:PLD: Set the header sanity to zero **/ if ( result == 0 ) hinfo->sanity = 0; /** Proceed to read, process and finish headers **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Getting headers",FL); if ( result == 0 ) result = MIMEH_headers_get( hinfo, f ); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Processing headers",FL); if ( result == 0 ) result = MIMEH_headers_process( hinfo, glb.headerline ); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Cleanup of headers",FL); if ( result == 0 ) result = MIMEH_headers_cleanup(); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: END [F=%p, hinfo=%p, sanity=%d]\n", FL, f, hinfo, hinfo->sanity); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_dump_defects Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Displays a list of the located defects -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_dump_defects( struct MIMEH_header_info *hinfo ) { int i; MIMEH_defect_description_array[MIMEH_DEFECT_MISSING_SEPARATORS] = strdup("Missing separators"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_FIELD_OCCURANCE] = strdup("Multiple field occurance"); MIMEH_defect_description_array[MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE] = strdup("Unbalanced boundary quote"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_BOUNDARIES] = strdup("Multiple boundries"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_COLON_SEPARATORS] = strdup("Multiple colon separators"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS] = strdup("Multiple equals separators"); MIMEH_defect_description_array[MIMEH_DEFECT_UNBALANCED_QUOTES] = strdup("Unbalanced quotes"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_QUOTES] = strdup("Multiple quotes"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_NAMES] = strdup("Multiple names"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_FILENAMES] = strdup("Multiple filenames"); for (i = 0; i < _MIMEH_DEFECT_ARRAY_SIZE; i++) { if (hinfo->defects[i] > 0) { LOGGER_log("Header Defect: %s: %d",MIMEH_defect_description_array[i],hinfo->defects[i]); } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_defect_count Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_defect_count( struct MIMEH_header_info *hinfo ) { return hinfo->header_defect_count; } //----------------------END p3scan-2.3.2/ripmime-1.4.0.6/MIME_headers.h0000644000175000001440000001345110347164530016411 0ustar jlaiusers#ifndef MIMEHEADERS #define MIMEHEADERS #define MIMEH_VERSION "200511170925" #define _CTYPE_RANGE 99 #define _CTYPE_UNSPECIFIED -1 #define _CTYPE_MESSAGE_START 1 #define _CTYPE_MESSAGE 1 #define _CTYPE_MESSAGE_END 100 #define _CTYPE_MULTIPART_START 100 #define _CTYPE_MULTIPART 100 #define _CTYPE_MULTIPART_MIXED 101 #define _CTYPE_MULTIPART_APPLEDOUBLE 102 #define _CTYPE_MULTIPART_RELATED 103 #define _CTYPE_MULTIPART_ALTERNATIVE 104 #define _CTYPE_MULTIPART_REPORT 105 #define _CTYPE_MULTIPART_SIGNED 106 #define _CTYPE_MULTIPART_END 199 #define _CTYPE_TEXT_START 200 #define _CTYPE_TEXT 200 #define _CTYPE_TEXT_PLAIN 201 #define _CTYPE_TEXT_UNKNOWN 202 #define _CTYPE_TEXT_HTML 203 #define _CTYPE_TEXT_END 299 #define _CTYPE_IMAGE_START 300 #define _CTYPE_IMAGE 300 #define _CTYPE_IMAGE_GIF 301 #define _CTYPE_IMAGE_JPEG 302 #define _CTYPE_IMAGE_PNG 303 #define _CTYPE_IMAGE_END 399 #define _CTYPE_AUDIO_START 400 #define _CTYPE_AUDIO 400 #define _CTYPE_AUDIO_END 499 #define _CTYPE_OCTECT 800 #define _CTYPE_RFC822 500 #define _CTYPE_TNEF 600 #define _CTYPE_APPLICATION 700 #define _CTYPE_APPLICATION_APPLEFILE 701 #define _CTYPE_UNKNOWN 0 #define _CTRANS_ENCODING_UNSPECIFIED -1 #define _CTRANS_ENCODING_B64 100 #define _CTRANS_ENCODING_7BIT 101 #define _CTRANS_ENCODING_8BIT 102 #define _CTRANS_ENCODING_QP 103 #define _CTRANS_ENCODING_RAW 104 #define _CTRANS_ENCODING_BINARY 105 #define _CTRANS_ENCODING_UUENCODE 106 #define _CTRANS_ENCODING_UNKNOWN 0 #define _CDISPOSITION_UNSPECIFIED -1 #define _CDISPOSITION_INLINE 100 #define _CDISPOSITION_ATTACHMENT 200 #define _CDISPOSITION_FORMDATA 300 #define _CDISPOSITION_UNKNOWN 0 #define _MIMEH_FOUND_FROM 100 #define _MIMEH_STRLEN_MAX 1023 #define _MIMEH_FILENAMELEN_MAX 128 #define _MIMEH_CONTENT_TYPE_MAX 128 #define _MIMEH_SUBJECTLEN_MAX 128 #define _MIMEH_CONTENT_DESCRIPTION_MAX 128 #define _MIMEH_CONTENT_TRANSFER_ENCODING_MAX 256 #define _MIMEH_CONTENT_DISPOSITION_MAX 256 #define _MIMEH_DEBUG_NORMAL 1 #define _MIMEH_DEBUG_PEDANTIC 10 #define _MIMEH_DEFECT_ARRAY_SIZE 100 // Errors to throw back #define MIMEH_ERROR_DISK_FULL 128 // Defects #define MIMEH_DEFECT_MULTIPLE_QUOTES 1 #define MIMEH_DEFECT_UNBALANCED_QUOTES 2 #define MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS 3 #define MIMEH_DEFECT_MULTIPLE_COLON_SEPARATORS 4 #define MIMEH_DEFECT_MULTIPLE_BOUNDARIES 5 #define MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE 6 #define MIMEH_DEFECT_MULTIPLE_FIELD_OCCURANCE 7 #define MIMEH_DEFECT_MISSING_SEPARATORS 8 #define MIMEH_DEFECT_MULTIPLE_NAMES 9 #define MIMEH_DEFECT_MULTIPLE_FILENAMES 10 struct MIMEH_header_info { char scratch[_MIMEH_STRLEN_MAX +1]; int content_type; char content_type_string[ _MIMEH_CONTENT_TYPE_MAX +1 ]; char content_description_string[ _MIMEH_CONTENT_DESCRIPTION_MAX +1 ]; char boundary[_MIMEH_STRLEN_MAX +1]; int boundary_located; char subject[_MIMEH_SUBJECTLEN_MAX +1]; char filename[_MIMEH_FILENAMELEN_MAX +1]; char name[_MIMEH_STRLEN_MAX +1]; /** 20041217-1601:PLD: New header fields to keep **/ char from[_MIMEH_STRLEN_MAX +1]; char date[_MIMEH_STRLEN_MAX +1]; char to[_MIMEH_STRLEN_MAX +1]; char messageid[_MIMEH_STRLEN_MAX +1]; char received[_MIMEH_STRLEN_MAX +1]; /** end of new fields **/ // Store multiple filenames struct SS_object ss_filenames; // Store multiple names struct SS_object ss_names; int content_transfer_encoding; char content_transfer_encoding_string[ _MIMEH_CONTENT_TRANSFER_ENCODING_MAX +1 ]; int content_disposition; char content_disposition_string[ _MIMEH_CONTENT_DISPOSITION_MAX +1 ]; int charset; int format; int file_has_uuencode; char uudec_name[_MIMEH_FILENAMELEN_MAX +1]; // UUDecode name. This is a post-decode information field. int current_recursion_level; // Malformed email reporting int defects[_MIMEH_DEFECT_ARRAY_SIZE]; int header_defect_count; // Special Exception flags int x_mac; // Set if the content type contains x-mac-* entries, which means a filename may contain /'s /** Header sanity level - indicates if any of the headers we apparently read are good **/ int sanity; /** 20051117-0932:PLD: Will be non-zero if email is MIME **/ int is_mime; }; #ifdef RIPMIME_V2XX struct MIMEH_header_node { struct MIMEH_header_info *header_list; struct MIMEH_header_node *next; }; struct MIMEH_email_info { char mailpack_name[1024]; struct MIMEH_header_node *headers; }; #endif int MIMEH_version(void); int MIMEH_init( void ); int MIMEH_set_debug( int level ); int MIMEH_set_verbosity( int level ); int MIMEH_set_verbosity_contenttype( int level ); int MIMEH_get_verbosity_contenttype( void ); int MIMEH_get_headers_sanity(void); int MIMEH_is_contenttype( int range_type, int content_type ); int MIMEH_set_mailbox( int level ); int MIMEH_set_doubleCR( int level ); int MIMEH_set_doubleCR_save( int level ); int MIMEH_get_doubleCR_save( void ); int MIMEH_set_headers_save( FILE *f ); int MIMEH_set_headers_nosave( void ); int MIMEH_get_headers_save( void ); char *MIMEH_get_headers_ptr( void ); int MIMEH_set_headers_save_original( int level ); char *MIMEH_get_headers_original_ptr( void ); int MIMEH_set_headers_original_save_to_file( FILE *f ); int MIMEH_get_doubleCR( void ); char *MIMEH_get_doubleCR_name( void ); int MIMEH_set_header_longsearch( int level ); int MIMEH_read_headers( FFGET_FILE *f ); int MIMEH_headers_get( struct MIMEH_header_info *hinfo, FFGET_FILE *f ); int MIMEH_headers_process( struct MIMEH_header_info *hinfo, char *headers ); int MIMEH_headers_cleanup(); int MIMEH_parse_headers( FFGET_FILE *f, struct MIMEH_header_info *hinfo ); int MIMEH_display_info( struct MIMEH_header_info *hinfo ); int MIMEH_set_webform( int level ); int MIMEH_set_outputdir( char *dir ); int MIMEH_set_defect( struct MIMEH_header_info *hinfo, int defect ); int MIMEH_dump_defects( struct MIMEH_header_info *hinfo ); int MIMEH_get_defect_count( struct MIMEH_header_info *hinfo ); int MIMEH_set_report_MIME( int level ); #endif p3scan-2.3.2/ripmime-1.4.0.6/LICENSE0000644000175000001440000000270410347164530015022 0ustar jlaiusersCopyright (c) 2003, PLD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the PLD nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. p3scan-2.3.2/ripmime-1.4.0.6/boundary-stack.c0000644000175000001440000003320310347164530017105 0ustar jlaiusers#include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "boundary-stack.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define BS_STRLEN_MAX 1024 #define BS_BOUNDARY_DETECT_LIMIT_DEFAULT 4 #define DBS if (glb.debug) struct BS_globals { int debug; int verbose; int syslogging; int errlogging; int count; int detect_limit; int hold_limit; int smallest_length; int have_empty_boundary; struct BS_node *boundarystack; char boundarystacksafe[BS_STRLEN_MAX]; }; struct BS_node { char *boundary; int boundary_length; int boundary_nhl; // length of boundary without hyphens struct BS_node *next; }; static struct BS_globals glb; /*-----------------------------------------------------------------\ Function Name : BS_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_init( void ) { glb.debug = 0; glb.verbose = 0; glb.syslogging = 1; glb.errlogging = 0; glb.count = 0; glb.detect_limit = BS_BOUNDARY_DETECT_LIMIT_DEFAULT; glb.hold_limit = 0; glb.boundarystack = NULL; glb.smallest_length = -1; glb.have_empty_boundary = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_set_hold_limit Returns Type : int ----Parameter List 1. int limit , how many boundary strings to hold ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_hold_limit( int limit ) { glb.hold_limit = limit; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_set_verbose Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } /*-----------------------------------------------------------------\ Function Name : BS_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : BS_set_boundary_detect_limit Returns Type : int ----Parameter List 1. int limit , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_boundary_detect_limit( int limit ) { if ((limit > 0)&&(limit < BS_STRLEN_MAX)) { glb.detect_limit = limit; } return glb.detect_limit; } /*-----------------------------------------------------------------\ Function Name : BS_clear Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_clear( void ) { struct BS_node *next; while (glb.boundarystack) { DBS LOGGER_log("%s:%d:BS_clear:DEBUG: Popping off %p",FL,glb.boundarystack); next = glb.boundarystack->next; free(glb.boundarystack->boundary); free(glb.boundarystack); glb.boundarystack = next; } glb.boundarystack = NULL; glb.count = 0; glb.smallest_length = -1; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_non_hyphen_length Returns Type : int ----Parameter List 1. char *boundary , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_non_hyphen_length( char *boundary ) { int count = 0; char *p = boundary; while (*p) { if (isalnum((int)*p)) count++; p++; }; return count; } /*-----------------------------------------------------------------\ Function Name : BS_push Returns Type : int ----Parameter List 1. char *boundary , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_push( char *boundary ) { struct BS_node *node; if ((glb.hold_limit > 0)&&(glb.count >= glb.hold_limit)) { DBS LOGGER_log("%s:%d:BS_push:DEBUG: Number of boundaries to hold is at limit (limit=%d)",FL,glb.hold_limit); return 0; } node = malloc(sizeof(struct BS_node)); DBS LOGGER_log("%s:%d:BS_push:DEBUG: head = %p, nn = %p boundary = '%s'",FL, glb.boundarystack, node, boundary); if (node) { node->next = glb.boundarystack; glb.boundarystack = node; glb.boundarystack->boundary = strdup(boundary); glb.boundarystack->boundary_length = strlen(glb.boundarystack->boundary); if (glb.boundarystack->boundary_length == 0) glb.have_empty_boundary = 1; glb.boundarystack->boundary_nhl = BS_non_hyphen_length(boundary); glb.count++; // Set the smallest length if (glb.smallest_length == -1) glb.smallest_length = glb.boundarystack->boundary_length; else if (glb.boundarystack->boundary_length < glb.smallest_length) glb.smallest_length = glb.boundarystack->boundary_length; DBS LOGGER_log("%s:%d:DEBUGX: smallest = %d, NHL = %d, boundary = '%s'",FL,glb.smallest_length, node->boundary_nhl, boundary); } else { LOGGER_log("%s:%d:BS_push:ERROR: Cannot allocate memory for boundary stack PUSH, %s", FL, strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : *BS_pop Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *BS_pop( void ) { struct BS_node *node = glb.boundarystack; if (glb.boundarystack) { glb.boundarystack = glb.boundarystack->next; PLD_strncpy(glb.boundarystacksafe,node->boundary, BS_STRLEN_MAX); free(node->boundary); free(node); glb.count--; } return glb.boundarystacksafe; } /*-----------------------------------------------------------------\ Function Name : *BS_top Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *BS_top( void ) { if (glb.boundarystack) { return glb.boundarystack->boundary; } else return NULL; } /*-----------------------------------------------------------------\ Function Name : BS_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_count( void ) { return glb.count; } /*-----------------------------------------------------------------\ Function Name : BS_is_long_enough Returns Type : int ----Parameter List 1. int blen , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_is_long_enough( int blen ) { if (glb.smallest_length == -1) return 0; if (blen >= glb.smallest_length) return 1; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_boundary_detect Returns Type : int ----Parameter List 1. char *needle, 2. char *haystack , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_boundary_detect( char *haystack, char *needle, int needle_length ) { int result=1; int current_start = glb.detect_limit; char *haystack_start; if ((glb.have_empty_boundary == 1)&&(needle_length < 1)) { result = strncmp(haystack,"--",2); DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: empty-boundary test, result = %d",FL, result); return result; } if ((needle_length < 1)&&(glb.have_empty_boundary == 0)) return 1; DBS LOGGER_log("%s:%d:BS_boundary_detect: needle='%s', length=%d, haystack='%s', shift-window=%d" ,FL ,needle ,needle_length ,haystack ,current_start ); haystack_start = haystack; while ((current_start-- > 0)&&(*haystack_start != '\0')) { DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: CMP '%s' to '%s'",FL, needle, haystack_start); if (strncmp( needle, haystack_start, needle_length )==0) { DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: Hit on compare",FL); result = 0; break; } haystack_start++; } return result; } /*-----------------------------------------------------------------\ Function Name : BS_cmp Returns Type : int ----Parameter List 1. char *boundary, the boundary we want to check to see if is in the stack 2. int len , the length of the boundary ------------------ Exit Codes : 1 == boundary found, 0 == no boundary found Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_cmp( char *boundary, int len ) { char testspace[128]; // was 1024 int testspacelen=127; // was 1023 int spin=1; int nhl=0; struct BS_node *node=glb.boundarystack; struct BS_node *nodetmp=NULL, *nodedel=NULL; if ((!boundary)||(glb.count == 0)) return 0; //if ((glb.smallest_length > 0)&&(len < glb.smallest_length)) return 0; if (BS_is_long_enough(len) == 0) return 0; nhl = BS_non_hyphen_length(boundary); DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: possible-boundary='%s', len=%d, smallest=%d, count=%d, NHL=%d" , FL , boundary , len , glb.smallest_length , glb.count , nhl ); // Crop the incoming string to fit in our testspace length if (len > testspacelen) len = testspacelen; // Copy the potential boundary into our testspace snprintf(testspace, testspacelen, "%s", boundary); // First, search through the stack looking for a boundary that matches // our search criterion // // When we do find one, we will jump out of this WHILE loop by setting // 'spin' to 0. while((node)&&(spin)) { // if (node->boundary_length <= len) if (node->boundary_nhl == nhl) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Comparing '%s' to '%s'", FL, boundary, node->boundary); // * 20040903-08H57:PLD: Set boundary length comparison from > 0 to >= 0 if ((node->boundary != NULL)&&(node->boundary_length >= 0)) { if ((BS_boundary_detect(testspace, node->boundary, node->boundary_length))==0) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Boundary HIT",FL); spin = 0; } } } if (spin != 0) node = node->next; } // If we have a hit on the matching, then, according // to nested MIME rules, we must "remove" any previous // boundaries // // We know that we had a HIT in matching if spin == 0, because // in our previous code block that's what we set spin to if if(spin==0) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Boundary hit on '%s' == '%s'",FL, boundary,node->boundary); // If our "HIT" node is /NOT/ the one on the top of the // stack, then we need to pop off and deallocate the nodes // PRIOR/Above the hit node. // // ie, if "NODE" is not the top, then pop off until we // do get to the node if (node != glb.boundarystack) { nodetmp = glb.boundarystack; while ((nodetmp)&&(nodetmp != node)) { // - Set the node to delete (nodedel) to the current temp // node (notetmp) // - Increment the nodetmp to the next node in the stack // - Free the node to delete (nodedel) nodedel = nodetmp; nodetmp = nodetmp->next; free(nodedel->boundary); free(nodedel); } glb.boundarystack = node; } return 1; } else { return 0; } return 0; } p3scan-2.3.2/ripmime-1.4.0.6/boundary-stack.h0000644000175000001440000000051110347164530017106 0ustar jlaiusers int BS_init( void ); int BS_set_verbose( int level ); int BS_set_debug( int level ); int BS_set_boundary_detect_limit( int limit ); int BS_set_hold_limit( int limit ); int BS_clear( void ); int BS_push( char *boundary ); char *BS_pop( void ); char *BS_top( void ); int BS_cmp( char *boundary, int len ); int BS_count( void ); p3scan-2.3.2/ripmime-1.4.0.6/README0000644000175000001440000000132310347164530014671 0ustar jlaiusersREADME---------------------------------------------------------------- ripMIME is a small program which has been developed as part of the commercial Xamime development (http://www.xamime.com). ripMIME is being presented here to the public under a BSD style licence (see the LICENCE file included in with this package). ripMIME has been written with one sole purpose in mind, to extract the attached files out of a MIME encoded email package. ripMIME was written by Paul L Daniels of PLDaniels. ripMIME is Copyright to Paul L Daniels. Any comments/suggestions/correspondence can be directed to Paul Daniels via email at pldaniels@pldaniels.com. ---------------------------------------------------------------------- p3scan-2.3.2/ripmime-1.4.0.6/uuencode.c0000644000175000001440000005061510347164530015774 0ustar jlaiusers/*------------------------------------------------------------------------ Module: /extra/development/xamime/xamime_working/ripmime/uuencode.c Author: Paul L Daniels Project: Xamime:ripMIME State: Stable Creation Date: Description: uuencode is a collection of functions to aid the decoding / encoding of UUENCODED data files. This module is primarily intended to be used with ripMIME rather than for 'stand alone' use. The biggest issue is that the interfaces to the decoding functions are too specific at this point for a more generic use. ------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "ffget.h" #include "filename-filters.h" #include "uuencode.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define UUENCODE_DEBUG_PEDANTIC 10 #define UUENCODE_DEBUG_NORMAL 1 #define UUENCODE_STRLEN_MAX 1024 // Debug precodes #define UUENCODE_DPEDANTIC ((glb.debug >= UUENCODE_DEBUG_PEDANTIC)) #define UUENCODE_DNORMAL ((glb.debug >= UUENCODE_DEBUG_NORMAL )) #define UUENCODE_VERBOSE ((glb.verbosity > 0 )) #define UUENCODE_WRITE_BUFFER_SIZE 4096 #define UUENCODE_WRITE_BUFFER_LIMIT 4000 static unsigned char uudec[256]={ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 \ }; struct UUENCODE_globals { int debug; int verbosity; int verbosity_contenttype; int decode; int doubleCR_mode; int (*filename_decoded_report)(char *, char *); // Pointer to our filename reporting function }; static struct UUENCODE_globals glb; int uuencode_error; // this contains the error code for parents to check if they receive a -1 /*-----------------------------------------------------------------\ Function Name : UUENCODE_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_init( void ) { glb.debug = 0; glb.verbosity = 0; glb.verbosity_contenttype = 0; glb.decode = 1; glb.doubleCR_mode = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_verbosity Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_verbosity( int level ) { glb.verbosity = level; return glb.verbosity; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_verbosity_contenttype( int level ) { glb.verbosity_contenttype = level; return glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_decode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_decode( int level ) { glb.decode = level; return glb.decode; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_doubleCR_mode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_doubleCR_mode( int level ) { glb.doubleCR_mode = level; return glb.doubleCR_mode; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { glb.filename_decoded_report = ptr_to_fn; return 0; } /*------------------------------------------------------------------------ Procedure: UUENCODE_is_uuencode_header ID:1 Purpose: Tries to determine if the line handed to it is a UUencode header. Input: Output: 0 = No 1 = Yes Errors: ------------------------------------------------------------------------*/ int UUENCODE_is_uuencode_header( char *line ) { struct PLD_strtok tx; char buf[UUENCODE_STRLEN_MAX]; char *bp,*fp; int result = 0; // If we're not supposed to be decoding UUencoded files, then return 0 if (glb.decode == 0) return 0; snprintf( buf, sizeof(buf), "%s", line ); bp = buf; // If you're wondering why we dont check for "begin ",it's because we dont know // if begin is followed by a \t or ' ' or some other white space // Also, check to see that we don't have a VCARD ( look to see if there's a trailing // colon after the BEGIN // // 2003-08-12:PLD:Added *(bp+6) test as recommended by Bernard Fischer to ensure there's more // data after the begin if ((bp)&&(strncasecmp(bp,"begin",5)==0)&&(*(bp+5)!=':')&&(isspace((int)*(bp+5)))&&(*(bp+6))) { fp = NULL; bp = PLD_strtok(&tx, buf, " \n\r\t"); // Get the begin if (bp) fp = PLD_strtok(&tx, NULL, " \n\r\t"); // Get the file-permissions if (fp) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_uuencode_header:DEBUG: PERMISSIONS = %s\n", FL, fp); if ((atoi(fp) == 0)||(atoi(fp) > 777)) // Maximum is 777, because R+W+X = 7 { result = 0; } else result = 1; } else { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_uuencode_header:WARNING: Cannot read permissions for UUENCODED data file (%s)\n", FL, line); } } return result; } /*------------------------------------------------------------------------ Procedure: UUENCODE_is_file_uuenc ID:1 Purpose: Tries to determine if a given file is UUEncoded, or at least contains a UUENCODED file to it. This should only be run -after- we've checked with is_file_mime() because if the file is MIME, then it'll be able to detect UUencoding within the normal decoding routines. Input: filename to test Output: 0 = not uuencoded 1 = _probably_ uuencoded. Errors: ------------------------------------------------------------------------*/ int UUENCODE_is_file_uuencoded( char *fname ) { int result = 0; int linecount = 0; int limit=20; char line[ UUENCODE_STRLEN_MAX ]; FILE *f; f = fopen(fname,"r"); if (!f) { LOGGER_log("%s:%d:UUENCODE_is_file_uuencoded:ERROR: cannot open file '%s' for reading (%s)", FL, fname,strerror(errno)); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } while ((linecount < limit)&&(fgets(line,1024,f))) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_file_uuencoded:DEBUG: Testing line '%s'\n", FL, line); if (UUENCODE_is_uuencode_header( line )) { result = 1; break; } linecount++; } fclose(f); return result; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_decode_uu Returns Type : int ----Parameter List 1. FFGET_FILE *f, Source Data Stream 2. char *unpackdir, Directory to prefix to our write output 3. char *input_filename, The fully pathed input filename, containing UU data 4. char *out_filename, Pointer to a buffer where we will write the filename of the UU data 5. int out_filename_size, out_filename buffers size 6. int decode_whole_file, 0 == only first segment, >0 == all 7. int keep , Keep the files we create, don't delete ------------------ Exit Codes : Returns the number of attachments decoded in the data Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_decode_uu( FFGET_FILE *f, char *unpackdir, char *input_filename, char *out_filename, int out_filename_size, int decode_whole_file, int keep ) { int filename_found = 0; char buf[ UUENCODE_STRLEN_MAX ]; char *bp = buf, *fn, *fp; int n, i, expected; char fullpath[ UUENCODE_STRLEN_MAX ]=""; struct PLD_strtok tx; unsigned char *writebuffer = NULL; unsigned char *wbpos; int wbcount = 0; int loop = 0; int buflen = 0; int filecount = 0; FFGET_FILE ffinf; // Local static FFGET struct used if *f is NULL FFGET_FILE *finf; // Points to either *f or &ffinf FILE *outf; int outfo =0; // set if outfile was opened. FILE *inf = NULL; int output_filename_supplied = 0; int start_found = 0; if ((out_filename != NULL)) { if ((out_filename[0] != '\0')) { output_filename_supplied = 1; } } bp = buf; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Starting.(input=%s,output=%s)\n", FL, input_filename,out_filename ); // If no FFGET_FILE param is passed to us directly, then we must create out own. if (!f) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: NULL FFGET source stream given to us, create our own.\n",FL); // Setup the full source-data file path. snprintf(fullpath, sizeof(fullpath),"%s", input_filename ); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Full INPUT file path set as '%s'\n", FL, fullpath); inf = fopen(fullpath,"r"); if (!inf) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: Cannot open file '%s' for reading (%s)", FL, fullpath, strerror(errno)); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } FFGET_setstream(&ffinf, inf); FFGET_set_watch_SDL( glb.doubleCR_mode ); finf = &ffinf; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Creation done. [FFGET-FILE=%p, FILE=%p]\n", FL, finf, inf); } else { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: File handle already exists to read from, using",FL); finf = f; } writebuffer = malloc( UUENCODE_WRITE_BUFFER_SIZE *sizeof(unsigned char)); if (!writebuffer) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: cannot allocate 100K of memory for the write buffer",FL); uuencode_error = UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY; return -1; } else { wbpos = writebuffer; wbcount = 0; } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Beginning.(%s)\n",FL,fullpath); while (!FFGET_feof(finf)) { filename_found = 0; // First lets locate the BEGIN line of this UUDECODE file // if (output_filename_supplied == 0) if (1) /** 20041105-23H00:PLD: Stepan Kasal - UUbegin patch **/ { while (FFGET_fgets(buf, sizeof(buf), finf)) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: BUFFER: \n%s\n", FL, buf); // Check for the presence of 'BEGIN', but make sure it's not followed by a // colon ( which indicates a VCARD instead of UUENCODE if ((strncasecmp(buf,"begin",5)==0)&&(buf[5] !=':')&&(isspace((int)buf[5]))) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Located BEGIN\n",FL); // Okay, so the line contains begin at the start, now, lets get the decode details fp = fn = NULL; bp = PLD_strtok(&tx, buf, " \n\r\t"); // Get the begin if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: BEGIN = '%s'\n", FL, bp); if (bp) fp = PLD_strtok(&tx, NULL, " \n\r\t"); // Get the file-permissions if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Permissions/Name = '%s'\n", FL, fp); if (fp) fn = PLD_strtok(&tx, NULL, "\n\r"); // Get the file-name if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Name = '%s'\n", FL, fn); if (!fn) { bp = fp; } else bp = fn; if ((!bp)&&(!f)) { LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: unable to obtain filename from UUencoded text file header", FL); if (writebuffer) free(writebuffer); fclose(inf); uuencode_error = UUENCODE_STATUS_CANNOT_FIND_FILENAME; return -1; } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Full path = (%s)\n",FL,bp); filename_found = 1; break; } // If line starts with BEGIN } // While more lines in the INPUT file. } /** 20041105-23H02:PLD: Stepan Kasal Patch **/ // Filename from header has precedence: if (output_filename_supplied != 0) { filename_found = 1; bp = out_filename; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Output filename set to '%s'",FL, bp); } // If we have a filename, and we have our bp as NON-null, then we shall commence // to decode the UUencoded data from the stream. if ((filename_found != 0)&&(bp)) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Located filename (%s), now decoding.\n", FL, bp); // Clean up the file name FNFILTER_filter( bp, out_filename_size ); // If our filename wasn't supplied via the params, then copy it over here if (output_filename_supplied == 0) snprintf( out_filename, out_filename_size, "%s", bp ); // Create the new output full path snprintf(fullpath, sizeof(fullpath), "%s/%s", unpackdir, bp ); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Filename = (%s)\n", FL, fullpath); outf = fopen(fullpath, "wb"); if (!outf) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: Cannot open file '%s' (%s)", FL, fullpath,strerror(errno)); if (writebuffer) free(writebuffer); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } else outfo = 1; // Allocate the write buffer. By using the write buffer we gain an additional 10% in performance // due to the lack of function call (fwrite) overheads // Okay, now we have the UUDECODE data to decode... wbcount = 0; wbpos = writebuffer; while (outf) { // for each input line FFGET_fgets(buf, sizeof(buf), finf); if (UUENCODE_DPEDANTIC) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Read line:\n%s",FL,buf); if (FFGET_feof(finf) != 0) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Short file (%s)\n",FL, fullpath); if (writebuffer != NULL) free(writebuffer); uuencode_error = UUENCODE_STATUS_SHORT_FILE; return -1; } // If we've reached the end of the UUencoding if (strncasecmp(buf,"end",3)==0) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: End of UUencoding detected\n",FL); break; } if ( !strpbrk(buf,"\r\n") ) { LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Excessive length line\n",FL); } // The first char of the line indicates how many bytes are to be expected n = uudec[(int)*buf]; // If the line is a -blank- then break out. if ((start_found == 0)&&((*buf == '\n')||(*buf == '\r'))) continue; else start_found =1 ; if ((start_found != 0)&&((n <= 0) || (*buf == '\n'))) break; // Calculate expected # of chars and pad if necessary expected = ((n+2)/3)<<2; buflen = strlen(buf) -1; for (i = buflen; i <= expected; i++) buf[i] = ' '; bp = &buf[1]; // Decode input buffer to output file. while (n > 0) { // In order to reduce function call overheads, we've bought the UUDecoding // bit shifting routines into the UUDecode main decoding routines. This should // save us about 250,000 function calls per Mb. // UUENCODE_outdec(bp, outf, n); char c[3]; int m = n; c[0] = uudec[(int)*bp] << 2 | uudec[(int)*(bp+1)] >> 4; c[1] = uudec[(int)*(bp+1)] << 4 | uudec[(int)*(bp+2)] >> 2; c[2] = uudec[(int)*(bp+2)] << 6 | uudec[(int)*(bp+3)]; if (m > 3) m = 3; if ( wbcount >= UUENCODE_WRITE_BUFFER_LIMIT ) { fwrite(writebuffer, 1, wbcount, outf); wbpos = writebuffer; wbcount = 0; } // Transfer the decoded data to the write buffer. // The reason why we use a loop, rather than just a set of if // statements is just for code-viewing simplicity. It's a lot // easier to read than some nested chain of if's for (loop = 0; loop < m; loop++) { *wbpos = c[loop]; wbpos++; wbcount++; } bp += 4; n -= 3; } // while (n > 0) } // While (1) if ((outfo)&&(wbcount > 0)) { fwrite(writebuffer, 1, wbcount, outf); } if (outfo) fclose(outf); // Call our reporting function, else, if no function is defined, use the default // standard call if ((UUENCODE_VERBOSE) && (output_filename_supplied == 0)) { if (glb.filename_decoded_report == NULL) { LOGGER_log("Decoded: %s\n", fullpath); } else { glb.filename_decoded_report( fullpath, (glb.verbosity_contenttype>0?"uuencoded":NULL) ); } } filecount++; } // If valid filename was found for UUdecode else { out_filename[0] = '\0'; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: No FILENAME was found in data...\n",FL); } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Segment completed\n",FL); // If this file was a result of the x-uuencode content encoding, then we need to exit out // as we're reading in the -stream-, and we dont want to carry on reading because we'll // end up just absorbing email data which we weren't supposed to. if ((f)&&( !decode_whole_file )) break; } // While !feof(inf) if (writebuffer) free(writebuffer); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Completed\n",FL); if (inf) fclose(inf); return filecount; } p3scan-2.3.2/ripmime-1.4.0.6/uuencode.h0000644000175000001440000000152310347164530015773 0ustar jlaiusers#define UUENCODE_STATUS_SHORT_FILE 100 #define UUENCODE_STATUS_CANNOT_OPEN_FILE 101 #define UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY 102 #define UUENCODE_STATUS_CANNOT_FIND_FILENAME 103 #define UUENCODE_STATUS_OK 0 extern int uuencode_error; int UUENCODE_init( void ); int UUENCODE_set_debug( int level ); int UUENCODE_set_verbosity( int level ); int UUENCODE_set_verbosity_contenttype( int level ); int UUENCODE_set_nodecode( int level ); int UUENCODE_set_decode( int level ); int UUENCODE_set_doubleCR_mode( int level ); int UUENCODE_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ); int UUENCODE_is_uuencode_header( char *line ); int UUENCODE_is_file_uuencoded( char *fname ); int UUENCODE_decode_uu( FFGET_FILE *f, char *unpackdir, char *input_filename, char *out_filename, int out_filename_size, int decode_whole_file, int keep ); p3scan-2.3.2/ripmime-1.4.0.6/generate-buildcodes.sh0000755000175000001440000000067410347164530020265 0ustar jlaiusers#!/bin/sh build_code=`date +%s` build_date=`date` build_box=`uname -a` #build_cmd=`history !-1` BCF='buildcodes.h' printf "\n// Autogenerated buildcode file - this will change on every make\n" > $BCF printf "#define BUILD_CODE \"$build_code\"\n" >> $BCF printf "#define BUILD_DATE \"$build_date\"\n" >> $BCF printf "#define BUILD_BOX \"$build_box\"\n" >> $BCF #printf "#define BUILD_CMD \"$build_cmd\"\n" >> $BCF printf "\n" >> $BCF exit 0 p3scan-2.3.2/ripmime-1.4.0.6/libmime-decoders.c0000644000175000001440000004437510347164530017377 0ustar jlaiusers#include #include #include #include #include #include #include #include #include #include #include "ffget.h" #include "pldstr.h" #include "logger.h" #include "libmime-decoders.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define MDECODE_ISO_CHARSET_SIZE_MAX 16 // Debug precodes #define MDECODE_DPEDANTIC ((glb.debug >= MDECODE_DEBUG_PEDANTIC)) #define MDECODE_DNORMAL ((glb.debug >= MDECODE_DEBUG_NORMAL )) #define DMD if ((glb.debug >= MDECODE_DEBUG_NORMAL)) /* our base 64 decoder table */ static unsigned char b64[256]={ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \ }; static unsigned char hexconv[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,\ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ }; struct MDECODE_globals { int debug; int verbose; int decode_qp; int decode_b64; }; static struct MDECODE_globals glb; int MDECODE_init( void ) { glb.debug = 0; glb.verbose = 0; glb.decode_qp = 1; glb.decode_b64 = 1; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_debug ID:1 Purpose: Sets the debug level for reporting in MIME Input: int level : What level of debugging to use, currently there are only two levels, 0 = none, > 0 = debug info Output: Errors: ------------------------------------------------------------------------*/ int MDECODE_set_debug( int level ) { glb.debug = level; return glb.debug; } int MDECODE_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } int MDECODE_set_decode_qp( int level ) { glb.decode_qp = level; return glb.decode_qp; } int MDECODE_set_decode_b64( int level ) { glb.decode_b64 = level; return glb.decode_b64; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_short64 ID:1 Purpose: Decodes a BASE64 encoded realm Input: char *realm : base64 encoded NUL terminated string Output: decoded data is written to the short64 char Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_short64( char *short64 ) { int i; int realm_size = strlen( short64 ); int stopcount = 0; /* How many stop (=) characters we've read in */ int c; /* a single char as retrieved using MDECODE_get_char() */ int char_count = 0; /* How many chars have been received */ char output[3]; /* The 4->3 byte output array */ char input[4]; /* The 4->3 byte input array */ char *outstring = short64; char_count = 0; while (char_count < realm_size) { /* Initialise the decode buffer */ input[0] = input[1] = input[2] = input[3] = 0; /* snatch 4 characters from the input */ for (i = 0; i < 4; i++) { /* get a char from the filestream */ c = *short64; short64++; /* assuming we've gotten this far, then we increment the char_count */ char_count++; /* if we detect the "stopchar" then we better increment the STOP counter */ if (c == '=') { stopcount++; } /* test for and discard invalid chars */ if (b64[c] == 0x80) { i--; continue; } /* do the conversion from encoded -> decoded */ input[i] = (char)b64[c]; } /* for */ /* now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data */ output[0] = (input[0] << 2) | (input[1] >> 4); output[1] = (input[1] << 4) | (input[2] >> 2); output[2] = (input[2] << 6) | input[3]; /* determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it */ if (i == 4) { for (i = 0; i < (3 -stopcount); i++){ *outstring = output[i]; outstring++; } /* copy our data across */ } /* if 4 chars were inputted */ } /* while more chars to proccess */ *outstring = '\0'; // Set the last char to NULL return 0; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_quoted_printable ID:1 Purpose: Decodes quoted printable encoded data. Input: char *line : \0 terminated string possibly containing quoted printable data int qpmode : Selects which decoding ruleset to use ( refer to RFC2047 ) Output: Decoded string is superimposed over the provided line parameter Returns: Returns the number of bytes decoded. ------------------------------------------------------------------------*/ int MDECODE_decode_quoted_printable( char *line, int qpmode, char esc_char ) { char c; /* The Character to output */ int op, ip; /* OutputPointer and InputPointer */ int slen = strlen(line); /* Length of our line */ DMD LOGGER_log("%s:%d:MDECODE_decode_quoted_printable:DEBUG: input string = '%s' Input length = %d\n",FL, line, slen); /* Initialise our "pointers" to the start of the encoded string */ ip=op=0; /* for every character in the string... */ for (ip = 0; ip < slen; ip++) { c = line[ip]; /* if we have the quoted-printable esc char, then lets get cracking */ if (c == esc_char) { /* if we have another two chars... */ if ((ip +1) < slen ) { int original_ip = ip; /* Is our next char a \n\r ? if it is, then we have to eliminate any further \r\n's etc so as to turn the =\n\r into a 'soft return', which basically means that we ignore it. Soft-breaks are used so we can fit our long lines into the requirement of a maximum of 76 characters per line. So we move the input-pointer along skipping each character without incrementing the output pointer. */ /** Absorb any trailing whitespaces **/ if (1) { char *w = &(line[ip +1]); while ((*w == '\t') || (*w == ' ')) {w++;ip++;} } /** Do we now have a line break ? **/ if (( line[ip +1] == '\n') || (line[ip +1] == '\r' )) { ip++; if ((ip+1 < slen)&&(( line[ip +1] == '\n') || (line[ip +1] == '\r' ))) { ip++; } continue; } else { /* if the characters following the '=' symbol are not of the \n or \r pair, then we will [currently] assume that the next two characters are in fact the hexadecimal encodings of the character we do want */ /** Revert to original position **/ ip = original_ip; /* convert our encoded character from HEX -> decimal */ if ( ip < slen-1 ) // was 2, proving - if there are 3 chars in string, =AB, slen = 3, ip = 1 { c = (char)hexconv[(int)line[ip+1]]*16 +hexconv[(int)line[ip+2]]; /* shuffle the pointer up two spaces */ ip+=2; } else { LOGGER_log("%s:%d:MIME_decode_quoted_printable:WARNING: Ran out of characters when decoding end of '%s'\n", FL, &line[ip] ); } } } /* if there were two extra chars after the ='s */ /* if we didn't have enough characters, then we'll make the char the * string terminator (such as what happens when we get a =\n */ else { /* 2002-12-16:18H31: changed from 'line[ip]' to 'line[op]' */ line[op] = '\0'; /* 2002-12-16:18H32: added break statement - if we're out of chars, then we quit the for loop */ break; } /* else */ } /* if c was a encoding char */ else if (( c == '_' ) && ( qpmode == MDECODE_QPMODE_ISO )) { // RFC2047 (Section 4.2.(2)(3)) says that if we encounter a '_' character in our ISO encodings then // we must convert that to a space ( as we are not allowed to have spaces in any c = ' '; } /* put in the new character, be it converted or not */ line[op] = c; /* shuffle up the output line pointer */ op++; } /* for loop */ /* terminate the line */ line[op]='\0'; DMD LOGGER_log("%s:%d:MDECODE_decode_quoted_printable:DEBUG: Output = '%s' Output length = %d\n", FL, line, strlen(line)); // 2003-01-26:PLD: Changed from (op -1) -=> op return op; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_text_line ID:1 Purpose: Decodes a line of text, checking for Quoted-Printable characters and converting them. Note - if the character converted is a \0 (after decoding) it shouldn't affect the calling parent because the calling parent should read back the returned string byte size and use fwrite() or other non-\0 affected writing/processing functions Input: char *line: pointer to the buffer/line we wish to convert/scan Output: int: size of final buffer in bytes. Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_qp_text( char *line ) { if (glb.decode_qp == 0) return strlen(line); return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '=' ); } int MDECODE_decode_qp_ISO( char *line ) { // return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_ISO, '=' ); return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '=' ); } int MDECODE_decode_multipart( char *line ) { return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '%' ); } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_ISO ID:1 Purpose: Decodes an ISO ( RFC2047 ) encoded string into native codepage dependent output Input: char *isostring : String containing =?code-page?encoding-type?string?= format int length : length of the string we're decoding Output: isostring is overwritten with the decoded string. Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_ISO( char *isostring, int size ) { char *start_pair, *end_pair; char *iso, *iso_copy; char encoding_type='-'; char encoding_charset[ MDECODE_ISO_CHARSET_SIZE_MAX ]; char *iso_start, *iso_end; int iso_decoded; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-string='%s'",FL,isostring); // Process of decoding the ISO encoded string sequence. // ( this process is repeated until we run out of ISO sequences ) // // 1. Check that the string has a =? sequence within it ( indicates the start of the ISO encoding // // 2. tokenise the sequence succeeding the =? token into its three (3) parts, namely the code-page, encoding-type and string respectively // // 3. decode the string based on the encoding type, Q = Quoted-Printable, B = BASE64 // iso_end = iso_start = NULL; start_pair = end_pair = NULL; iso_copy = malloc( sizeof(char) *( size +1 ) ); do { iso_decoded = 0; start_pair = strstr( isostring, "=?" ); // if ( start_pair ) end_pair = strstr( start_pair +2, "?=" ); if (( start_pair != NULL )) { iso_start = start_pair; // There's probably a better way of doing this, but, for us to find the end of this // particular 'ISO' sequence, we need to hop past 3 more ?'s ( assuming we've already // found the first one. DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO start = %s",FL,iso_start); iso_end = strchr( iso_start +strlen("=?"), '?' ); // Jump past the encoding if (iso_end) iso_end = strchr( iso_end +1, '?' ); // Jump past the Q or B if (iso_end) iso_end = strpbrk( iso_end +1, "?\n\r\t;" ); // dropped the SPACE here. if ((iso_end != NULL)&&(*iso_end == '?')) iso_end+=2; if ( (iso_start) && (iso_end) ) { char *token_end; char restore_char='\0'; // Copy the Encoding page/code. iso = iso_start +strlen("=?"); token_end = strchr(iso,'?'); if (token_end) *token_end = '\0'; snprintf( encoding_charset, sizeof( encoding_charset ), "%s", iso); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO char set = '%s'",FL,encoding_charset); iso = token_end +1; // Get the encoding _type_ (BASE64/QuotedPrintable etc) token_end = strchr(iso,'?'); encoding_type = *iso; iso = token_end +1; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO encoding char = '%c'",FL,encoding_type); // Get the encoded string token_end = strpbrk(iso,"?;\n\r\t"); //DROPPED THE SPACE here if (token_end != NULL) { if ((*token_end != '?')&&(*token_end != ';')) { restore_char = *token_end; } *token_end = '\0'; } if (iso) { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Encoded String = '%s'\n", FL, iso ); switch ( encoding_type ) { case MDECODE_ISO_ENCODING_Q: case MDECODE_ISO_ENCODING_q: DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoding filename using Quoted-Printable (%s)\n", FL, iso); MDECODE_decode_qp_ISO(iso); iso_decoded = 1; break; case MDECODE_ISO_ENCODING_B: case MDECODE_ISO_ENCODING_b: DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoding filename using BASE64 (%s)\n", FL, iso); MDECODE_decode_short64( iso ); iso_decoded = 1; break; default: if (glb.verbose) LOGGER_log("%s:%d:MDECODE_decode_ISO:ERROR: The encoding character '%c' is not a valid type of encoding\n", FL, encoding_type ); } // If we decoded the string okay, then we need to recompose the string if ( iso_decoded == 1 ) { char *new_end_pos; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoded String = '%s'\n", FL, iso ); *iso_start = '\0'; // Terminate the original string before the start of the ISO data // Because sometimes ISO strings are broken over multiple lines // due to wrapping requirements of RFC(2)822, we need to // sniff out these tab or spaces and crop them out of our // final ISO string. We cannot simply search for the next // =? sequence using strstr() because it might traverse // beyond the end of the current 'line' (ie, \r\n termination) if (token_end) { iso_end = token_end +1; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: iso_end = '%20s'",FL, iso_end); while ((*iso_end == '?')||(*iso_end == '=')) iso_end++; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: iso_end = '%20s'",FL, iso_end); new_end_pos = iso_end; while ((*new_end_pos == ' ')||(*new_end_pos == '\t')) new_end_pos++; if (strncmp(new_end_pos,"=?",2)==0) iso_end = new_end_pos; } else { iso_end = NULL; } DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-END = '%20s'",FL,iso_end); /** We now have the string split into 3 peices, ** isostring = pointing to the start ** iso = newly decoded string ** iso_end = start of string after the non-decoded ISO portion **/ /** Generate new string using the decoded ISO to a temporary string **/ if (restore_char != '\0') { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Recomposing string with restore-char of '%c'",FL,restore_char); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-end (start of end of string) is \n%s",FL,iso_end); snprintf( iso_copy, size, "%s%s%c%s", isostring, iso, restore_char, (iso_end?iso_end:"") ); } else { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Recomposing string with NO restore-char",FL,restore_char); snprintf( iso_copy, size, "%s%s%s", isostring, iso, (iso_end?iso_end:"") ); } /** Switch the new headers over to the original headers again **/ snprintf( isostring, size, "%s", iso_copy ); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: New ISO string = \n%s",FL,isostring); } } } } // if (iso_start) } while (iso_decoded == 1 ); if (iso_copy) free(iso_copy); return 0; } //------------END libmime-decoders.c p3scan-2.3.2/ripmime-1.4.0.6/libmime-decoders.h0000644000175000001440000000151610347164530017372 0ustar jlaiusers /* Debug levels */ #define MDECODE_DEBUG_PEDANTIC 10 #define MDECODE_DEBUG_NORMAL 1 /* Filename Encoding characters */ #define MDECODE_ISO_ENCODING_Q 'Q' #define MDECODE_ISO_ENCODING_q 'q' #define MDECODE_ISO_ENCODING_B 'B' #define MDECODE_ISO_ENCODING_b 'b' /* Quoted-Printable decoding modes */ #define MDECODE_QPMODE_STD 0 #define MDECODE_QPMODE_ISO 1 int MDECODE_set_debug( int level ); int MDECODE_set_verbose( int level ); int MDECODE_set_decode_qp( int level ); int MDECODE_set_decode_b64( int level ); int MDECODE_init( void ); int MDECODE_decode_quoted_printable( char *line, int qpmode, char esc_char ); int MDECODE_decode_short64( char *short64 ); int MDECODE_decode_multipart( char *line ); int MDECODE_decode_qp_text( char *line ); int MDECODE_decode_qp_ISO( char *line ); int MDECODE_decode_ISO( char *isostring, int size ); p3scan-2.3.2/ripmime-1.4.0.6/ffget.c0000644000175000001440000004213610347164530015257 0ustar jlaiusers/*------------------------------------------------------------------------ Module: ffget.c Author: pldaniels Project: ripmime State: development Creation Date: 14/05/2001 Description: ffget is a small module which will be used to (we hope) speed up the fgetc() routine by line-buffering up first. 12/11/2002: Corrected input buffer termination with fgets where the last line of the input file does not terminate with a \n or \r. 27/09/2001: Added SGI specific compile time changes from char -> short contributed by Don Lafontaine ------------------------------------------------------------------------*/ #include #include #include #include #include #include "logger.h" #include "ffget.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define FFGET_VERSION "1.0.0.4" #define FFGET_LASTUPDATED "09/08/2004" #define FFGET_DNORMAL ((FFGET_debug >= _FFGET_DEBUG_NORMAL )) #define FFGET_DPEDANTIC ((FFGET_debug >= _FFGET_DEBUG_PEDANTIC)) /* GLOBALS */ int ffget_linesize=0; int FFGET_doubleCR = 0; int FFGET_SDL_MODE = 0; // Single Char Delimeter int FFGET_SDL_WATCH = 0; // Set if we want to watch for double-CR exploits int FFGET_ALLOW_NUL = 0; // Dont Convert \0's to spaces. int FFGET_debug = 0; char SDL_MODE_DELIMITS[]="\n\r"; char NORM_MODE_DELIMITS[]="\n"; char *DELIMITERS=SDL_MODE_DELIMITS; /*------------------------------------------------------------------------ Procedure: FFGET_set_watch_SDL ID:1 Purpose: Set/Unset the flag to indicate that we should be watching out for a double-CR potential exploit mode when decoding files. Input: int level: 0 = don't watch, !0 = watch Output: Errors: ------------------------------------------------------------------------*/ int FFGET_set_watch_SDL( int level ) { FFGET_SDL_WATCH = level; return FFGET_SDL_WATCH; } /*-----------------------------------------------------------------\ Function Name : FFGET_set_allow_nul Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This tells the FFGET_raw() if it needs to remove \0's or not in the data it reads. This was added so as to ensure that \0 sequences were preserved in form-data from WWW pages. -------------------------------------------------------------------- Changes: Added 2004-Aug-09 by PLD. \------------------------------------------------------------------*/ int FFGET_set_allow_nul(int level ) { FFGET_ALLOW_NUL = level; return FFGET_ALLOW_NUL; } /*------------------------------------------------------------------------ Procedure: FFGET_set_debug ID:1 Purpose: Set debugging report/verbosity level Input: level Output: Returns the level set Errors: ------------------------------------------------------------------------*/ int FFGET_set_debug( int level ) { FFGET_debug = level; return FFGET_debug; } /*------------------------------------------------------------------------ Procedure: FFGET_getnewblock ID:1 Purpose: Reads a new block of data from the input file Input: FFGET_FILE record Output: Returns number of bytes read Errors: ------------------------------------------------------------------------*/ int FFGET_getnewblock( FFGET_FILE *f ) { int i; int bs = 0; char *p; // We read the maximum of FFGET_BUFFER_MAX -2, because later, when we // use fgets(), we may need to read in an /additional/ single byte // and if we dont allocate spare room, we may have a buffer overflow if (f->FILEEND > 0) { f->endpoint = f->buffer; f->startpoint = f->buffer +1; f->FFEOF = 1; return 0; } else { bs = fread( f->buffer, 1, _FFGET_BUFFER_MAX -2, f->f ); if (bs < (_FFGET_BUFFER_MAX -2)) { if (feof(f->f)) { f->FILEEND = 1; } else { LOGGER_log("%s:%d:FFGET_getnewblock:ERROR: File read failed with error:%s", FL, strerror(errno)); return 0; } } if (bs > 0) { // If we read in some data, then adjust the buffer to deal with it // // First we set the start point back to the start of the buffer, // then we set the end point to be the start +datasize we read, -1 // then we adjust the total bytes read (for the sake of record keeping // though it has no /real/ purpose) // f->buffer[bs] = '\0'; //20040208-1703:PLD:JS f->startpoint = f->buffer; f->endpoint = f->startpoint +bs -1; f->bytes += bs; // Check the buffer for poisioning \0's // As these routines are being used for 7-bit valid text data, // we have to filter out any nasty \0's. if (FFGET_ALLOW_NUL == 0) { p = f->startpoint; for (i = 0; i < bs; i++) { if (*p == '\0') *p = ' '; p++; } *p = '\0'; } if (FFGET_DPEDANTIC) LOGGER_log("%s:%d:FFGET_getnewblock:DEBUG-PEDANTIC: Size: %ld bytes\n", FL, f->bytes); } } return bs; } /*------------------------------------------------------------------------ Procedure: FFGET_presetbuffer ID:1 Purpose: Presets the FFGET buffer with defined data Input: FFGET_FILE record Buffer to get data from Quantity of data Output: None Errors: ------------------------------------------------------------------------*/ int FFGET_presetbuffer( FFGET_FILE *f, char *buffer, int size ) { if (size > _FFGET_BUFFER_MAX) size = _FFGET_BUFFER_MAX; memcpy(f->buffer,buffer,size); f->startpoint = buffer; f->endpoint = buffer +size; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_setstream ID:1 Purpose: Sets the FILE * stream to the FFGET_FILE record Input: FFGET_FILE record Stream to use. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_setstream( FFGET_FILE *f, FILE *fi ) { memset(f,0,sizeof(FFGET_FILE)); // be pedantic - clear the struct f->f = fi; f->bytes = 0; f->endpoint = f->buffer; f->startpoint = f->endpoint +1; f->trueblank = 0; f->ungetcset = 0; f->lastchar = '\0'; memset(f->buffer,'\0',_FFGET_BUFFER_MAX +1); f->c = '\0'; f->FFEOF = 0; f->FILEEND = 0; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_closestream ID:1 Purpose: Closes the stream contained in a FFGET record Input: FFGET record containing the stream to close. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_closestream( FFGET_FILE *f ) { f->startpoint = f->endpoint = NULL; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_feof ID:1 Purpose: Returns the status of FFGET's EOF Input: FFGET record Output: EOF status (0 == NOT eof, 1 == EOF has been reached) Errors: ------------------------------------------------------------------------*/ int FFGET_feof( FFGET_FILE *f ) { return f->FFEOF; } /*------------------------------------------------------------------------ Procedure: FFGET_ungetc ID:1 Purpose: Pushes back into the buffer (effectively) a single character Input: FFGET record Character to retain for the next read. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_ungetc( FFGET_FILE *f, char c ) { f->c = c; f->ungetcset = 1; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_getc ID:1 Purpose: Gets a single character from the FFGET buffer Input: FFGET record Output: Single character from the buffer, or EOF if end of file. Errors: ------------------------------------------------------------------------*/ #ifdef sgi short FFGET_fgetc( FFGET_FILE *f ) #else char FFGET_fgetc( FFGET_FILE *f ) #endif { int c; if (f->ungetcset) { f->ungetcset = 0; return f->c; } if ((!f->startpoint)||(f->startpoint > f->endpoint)) { FFGET_getnewblock(f); } if (f->FFEOF == 0) { c = *f->startpoint; f->startpoint++; } else { c = EOF; } return c; } /*------------------------------------------------------------------------ Procedure: FFGET_fgets ID:1 Purpose: Gets a single line from the input buffer. The line can be either \r \n \r\n terminated based on the status flags set/unset by previous reads. This function is the key to making tools like ripMIME be able to see double-vision, that is, to see emails like Outlook does, and also like RFC. Input: line: Buffer to write to max_size: Maximum number of bytes to write to line. f: FFGET record to use to read. Output: Pointer to line. Errors: ------------------------------------------------------------------------*/ char *FFGET_fgets( char *linein, int maxsize, FFGET_FILE *f ) { char *line = linein; char *crlfpos = NULL; int charstoCRLF = 0; int chardiff = 0; int result = 0; int max_size = maxsize; f->trueblank = 0; if (f->FFEOF != 0) { return NULL; } if ((FFGET_SDL_WATCH > 0)||(FFGET_SDL_MODE != 0)) { DELIMITERS = SDL_MODE_DELIMITS; } else DELIMITERS = NORM_MODE_DELIMITS; // fprintf(stderr,"FFGET_called, SDLMODE = %d, Offset = %d, maxsize = %d, DATA left = %d, first char is '%02X'\n", FFGET_SDL_MODE, (f->startpoint -f->buffer), max_size, (f->endpoint -f->startpoint)+1, (int)(*f->startpoint)); max_size = maxsize = maxsize -2; // memset(line, 0, max_size+1); // If we dont have enough data in the buffer to fill up the fgets request // we'll have to do a two step fill //fprintf(stderr,"DATA Reminianing : %d\n", (int)(f->endpoint -f->startpoint)+1); if ((f->startpoint > f->endpoint)) { result = FFGET_getnewblock(f); if (result == 0) { *linein = '\0'; return NULL; } } // This loop does not go around too many times, once, maybe twice max. while ((max_size > 0)&&(f->FFEOF == 0)) { crlfpos = strpbrk( f->startpoint, DELIMITERS); if (crlfpos) { // if our next char is a CR or LF, then pick it up and // return it with the line. NOTE - this is to deal with // CRLF pairings which are common on DOS files. In fact, // this is a case of where UNIX is actually -wrong-. It // should have also used CRLF pairing to mark line ends, but // someone obviously (and understandably, to save space) // thought they'd leave make LF imply a CR as well. // Well done... another bugger up in life. // The logic of this nested IF statement is as follows... // If we do have another char available... // and if the pairing is not \n\n (which should be treated as two lines // and if the next char is a \n or a \r, // THEN we should increment the end of line pointer so that we // include the additional \n or \r into the line we're going to // return // If we are NOT in the Single-delimeter mode (SDL_MODE), and the next // char is available, then commence the delimeter testing routines if ((0==f->FILEEND)&&(0==f->FFEOF)&&( ((crlfpos +1) > f->endpoint))) { // We have an EOL character, get 1 more from the stream to test the next character int c; c = fgetc(f->f); if (c==EOF) { // fprintf(stderr,"EOF hit due to fgetc()\n"); f->FILEEND = 1; } else { if (c == '\0') c = ' '; // Check for character value vadality if ((c > 0) && (c <= 255)) { f->endpoint++; *(f->endpoint) = c; *(f->endpoint+1) = '\0'; } } } // If (crlfpos +1) is /not/ within our buffer bounds // If the next char from our CRLF pickup is within the bounds of // our endpoint, then proceed to test the CRLF combo if ( ((crlfpos +1) <= f->endpoint)) { // fprintf(stderr,"Found '%02X' [next is '%02X']\n",*crlfpos, *(crlfpos+1)); if ( *crlfpos == '\n' ) { if ( *(crlfpos +1) == '\r' ) { crlfpos++; } } // If our combo starts with a \r, then test it to see // if we have another \r after it, in which case, we // turn on SINGLE_DELIMETER_MODE. if ( (*crlfpos == '\r') ) { if ( *(crlfpos +1) == '\r' ) { // A \r\r sequence has just been detected, set our doubleCR flag // so that MIME_headers can read it and react accordingly. // Look out for single \r's from here on, as they are now seen as // EOL markers in Outlook. FFGET_doubleCR=1; FFGET_SDL_MODE=1; crlfpos++; } else if ( *(crlfpos +1) == '\n' ) { // If we see a \n after our \r, then treat this as a single // line delimeter if we are NOT in Single Delimeter mode if (!FFGET_SDL_MODE) { crlfpos++; }// 20040208-1706:PLD //crlfpos++;// 20040208-1706:PLD // 20040306-0003:PLD - this line causes a CRCR test to fail; mailpack.virus.badtrans } else { // If we saw a \r, but then there was no other EOL type char (\r or \n) // then switch to SDL mode (Single delimeter). FFGET_SDL_MODE=1; } } // If combo starts with a \r } // If crlfpos +1 is within the bounds of our buffer. // Determine how many characters/bytes there are from the startpoint, // to the CRLF position. charstoCRLF = crlfpos -f->startpoint; // If the number of chars is -less- than that of the maximum line read // size which our calling program has specified, then we set the max_size // to be the number of chars. //DEBUG fprintf(stderr, "MAX_size = %d, charstoCRLF = %d\n", max_size, charstoCRLF); if ((charstoCRLF >= 0)&&(charstoCRLF < max_size)) max_size = charstoCRLF; } // If CRLF pos found. // else crlfpos = (f->endpoint +1); // If the buffer amount remaining in our FFGET buffer is greater than // the maximum size available in our line buffer, then we // only copy the max_size amount across if (( f->endpoint -f->startpoint) >= max_size) { if (max_size < 0) LOGGER_log("%s:%d:FFGET_fgets:ERROR: Max size < 0\n", FL); memcpy(line, f->startpoint, max_size +1);//+1 f->startpoint += (max_size +1); //+1 *(line +max_size +1) = '\0'; //+1 max_size = 0; } else { // else, if the amount of data available is /LESS/ than what we can // accept in the line buffer then copy what remains out to the line // buffer and then tack on the results of a new read. chardiff = f->endpoint -f->startpoint; // fprintf(stderr,"CHARDiff = %d, FFEOF = %d, FILEEND = %d\n",chardiff, f->FFEOF, f->FILEEND); if (chardiff >= 0) { memcpy(line, f->startpoint, chardiff +1); *(line +chardiff +1) = '\0'; // 12-11-2002: Added this line to terminate the input buffer incase it wasn't already flushed with \0's line += (chardiff +1); max_size -= (chardiff +1); f->startpoint = f->endpoint +1; if (max_size < 0) max_size = 0; } FFGET_getnewblock(f); } // If there wasn't enough data to satisfy ends. } // While we've got space to fill, and we've got data to read line = linein; f->trueblank = 0; if ((f->lastchar == '\n')||(f->lastchar == '\r')) { if ((line[0] == '\n')||(line[0] == '\r')) { f->trueblank = 1; } } f->lastchar = line[strlen(line) -1]; // LOGGER_log("%s:%d:LINE='%s'",FL,linein); return linein; } /*------------------------------------------------------------------------ Procedure: FFGET_raw ID:1 Purpose: This is a hybrid binary-read and fgets type read. This function reads data from the input buffer until it encounters a \r \n \r\n at which point it will return to the calling parent with its buffer containing that line. This is required so that we dont miss any boundary specifiers which are located on new-lines. Input: f: FFGET record buffer: memory location to write data to max: maximum holding capacity of the raw buffer Output: Returns the number of bytes placed into the buffer. Errors: ------------------------------------------------------------------------*/ int FFGET_raw( FFGET_FILE *f, unsigned char *buffer, int max ) { unsigned char c; // read buffer int bytestogo = 0; int count = 0; // How many bytes read // Special situation here, if we have a return from MIME_headers which indicates // that we have data in a MIMEH_pushback, then we need to process that first, before we // go back into the data file. if ((!f->startpoint)||(f->startpoint > f->endpoint)) { bytestogo = FFGET_getnewblock(f); } else { bytestogo = f->endpoint -f->startpoint +1; } // Whilst we've got less bytes than the maximum availabe // for the buffer, we keep on reading // while (count < max) { if (!bytestogo) { bytestogo = FFGET_getnewblock(f); } if (!f->FFEOF) { c = *f->startpoint; f->startpoint++; *buffer = c; buffer++; count++; bytestogo--; // If we get a line delimeter, check to see that the next char (which is now // pointed to at f->startpoint isn't a delimeter as well which perhaps we should // be including in our line were' going to return // // 25/05/02 - Silly mistake, I had (!\n || !\r) when it should be && (ie, if the next // char is NEITHER of the \n or \r chars, then break. // if ((c == '\n')||(c == '\r')) { if ( (*(f->startpoint) != '\n') && (*(f->startpoint) != '\r') ) break; } } else break; } *buffer = '\0'; return count; } //--------------END. p3scan-2.3.2/ripmime-1.4.0.6/ffget.h0000644000175000001440000000224410347164530015260 0ustar jlaiusers /* DEFINES */ #define _MAX_LINE_LEN 1024 #define _FFGET_BUFFER_MAX (8192*8) #define _FFGET_DEBUG_NORMAL 1 #define _FFGET_DEBUG_PEDANTIC 10 struct _FFGET_FILE { char buffer[_FFGET_BUFFER_MAX +1]; char *startpoint; char *endpoint; int FILEEND; int FFEOF; char c; unsigned long int bytes; int ungetcset; int trueblank; char lastchar; FILE *f; }; typedef struct _FFGET_FILE FFGET_FILE; // Special Flag to indicate a Double CR Line. extern int FFGET_doubleCR; extern int FFGET_SDL_MODE; // Single Char Delimeter extern char SDL_MODE_DELIMITS[]; extern char NORM_MODE_DELIMITS[]; extern char *DELIMITERS; int FFGET_setstream( FFGET_FILE *f, FILE *fi ); #ifdef sgi short FFGET_fgetc( FFGET_FILE *f ); #else char FFGET_fgetc( FFGET_FILE *f ); #endif int FFGET_closestream( FFGET_FILE *f ); int FFGET_ungetc( FFGET_FILE *f, char c ); int FFGET_presetbuffer( FFGET_FILE *f, char *buffer, int size ); char *FFGET_fgets( char *linein, int max_size, FFGET_FILE *f ); int FFGET_raw( FFGET_FILE *f, unsigned char *buffer, int max ); int FFGET_feof( FFGET_FILE *f ); int FFGET_getnewblock( FFGET_FILE *f ); int FFGET_set_watch_SDL( int level ); int FFGET_set_allow_nul(int level ); p3scan-2.3.2/ripmime-1.4.0.6/filename-filters.c0000644000175000001440000001427410347164530017414 0ustar jlaiusers/*------------------------------------------------------------------------ Module: /extra/development/xamime/xamime_working/ripmime/filename-filters.c Author: Paul L Daniels Project: ripMIME State: Release Creation Date: 01 Jan 03 Description: Filename Filters is a module which is designed to check and 'safety-enhance' filenames which are passed to it. This may include things like removing directory risers ( ../.. ), root directory attempts ( / ), and parameter passing. ------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "pldstr.h" #include "logger.h" #include "filename-filters.h" #ifndef FL #define FL __FILE__, __LINE__ #endif #define FNFILTER_DEBUG_PEDANTIC 10 #define FNFILTER_DEBUG_NORMAL 1 // Debug precodes #define FNFILTER_DPEDANTIC ((glb.debug >= FNFILTER_DEBUG_PEDANTIC)) #define FNFILTER_DNORMAL ((glb.debug >= FNFILTER_DEBUG_NORMAL )) #define DFN if ((glb.debug >= FNFILTER_DEBUG_NORMAL)) struct FNFILTER_globals { int debug; int verbose; int paranoid; int x_mac; }; static struct FNFILTER_globals glb; int FNFILTER_init( void ) { glb.debug = 0; glb.verbose = 0; glb.paranoid = 0; glb.x_mac = 0; return 0; } int FNFILTER_set_debug( int level ) { glb.debug = level; return glb.debug; } int FNFILTER_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } int FNFILTER_set_mac( int level ) { glb.x_mac = level; return glb.x_mac; } int FNFILTER_set_paranoid( int level ) { glb.paranoid = level; return glb.paranoid; } /*------------------------------------------------------------------------ Procedure: quick_clean_filename ID:1 Purpose: Removes non-7bit characers from the filename Input: char *fname: Null terminated string Output: Errors: ------------------------------------------------------------------------*/ int FNFILTER_paranoid_filter( char *fname, int size ) { char tmp[1024]; char *p; /* Scan for . and .. filenames ** 20040727-12H54 ** Patch supplied by Marco Ariano ** Patch modified by Paul L Daniels ** */ if ((1 == size)&&('.' == *fname)) { *fname = '_'; return 0; } else if ((2 == size)&&(0 == strncmp(fname,"..",2))) { snprintf(fname,3,"__"); return 0; } /* scan out any directory separators */ p = strrchr(fname,'/'); if (p) { // Check to see that this seperator isn't the -last- char in the string if (*(p+1) == '\0') *p = '\0'; else { p++; PLD_strncpy(tmp,p,sizeof(tmp)); PLD_strncpy(fname,tmp,size); } } else if ( (p = strrchr(fname,'\\'))) { // Check to see that this seperator isn't the -last- char in the string if (*(p+1) == '\0') *p = '\0'; else { p++; PLD_strncpy(tmp,p,sizeof(tmp)); PLD_strncpy(fname,tmp,size); } } if ( glb.paranoid > 0 ) { // If we're really paranoid, then we go along and convert anything we don't like // the look of into 7-bit // // These days we shouldn't be using this any more as there are many filenames // which require > 7-bit charsets. while (*fname) { if( !isalnum((int)*fname) && (*fname != '.') ) *fname='_'; if( (*fname < ' ')||(*fname > '~') ) *fname='_'; fname++; } } return 0; } /*------------------------------------------------------------------------ Procedure: MIME_decode_filename ID:1 Purpose: Removed spurilous characters from filename strings. Input: char *fname: null terminated character string Output: Errors: ------------------------------------------------------------------------*/ int FNFILTER_filter( char *fname, int size ) { int fnl; char tmp[1024]; char *p; if (fname == NULL) return 0; fnl = strlen(fname); DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: fname[%d chars] = '%s'\n", FL, fnl, fname ); /** If we're handling a Mac file, prefilter **/ if (glb.x_mac == 1) { char *q = fname; DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Filtering x-mac filename '%s'",FL,fname); while (*q) { if (*q == '/') *q = '-'; /** Convert the Mac / separator to a hyphen **/ q++; } DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: x-mac filename is now '%s'",FL,fname); } /* We only look at trimming the quotes off a filename if it has more than 2 chars * because obviously we'll need to strip off 2 chars (leading and finishing quote) */ if ( fnl > 2 ) { /* if the MIME_filename starts and ends with "'s */ if ((fname[0] == '\"') && (fname[fnl-1] == '\"')) { if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Trimming quotes off filename\n", FL ); /* reduce the file namelength by two*/ fnl = fnl -2; // 17-11-2002: was =-2, thanks to Vasily Chernikov for spotting the glaring error! /* shuffle the MIME_filename chars down */ memmove(fname,fname+1,fnl); /* terminate the string */ fname[fnl] = '\0'; if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Trimming filename done, fname = '%s'\n", FL, fname ); } /* if */ } /* if */ p = strrchr(fname,'/'); if (p) { p++; PLD_strncpy( tmp, p, sizeof(tmp) ); PLD_strncpy( fname, tmp, size); } else { // Check for Windows/DOS backslash seperator p = strrchr( fname, '\\' ); if ( p ) { if ( *(p+1) != '"' ) { p++; PLD_strncpy( tmp, p, sizeof(tmp) ); PLD_strncpy( fname, tmp, size ); } } } // Scan for ? symbols - these are often used to make the email client pass paremeters to the filename // Check though to see that the previous character is not a '=' symbol, because if it is, then we // actually have an ISO encoded filename p = strchr( fname, '?' ); if (p != NULL) { if (p > fname) { if (*(p-1) != '=') { *p = '\0'; } else { // leave the ? alone, as it's part of an ISO encoded filename } } else { // First char of the filename is a '?', change this to a hypen. *p = '-'; } } if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Starting paranoia filter\n", FL ); FNFILTER_paranoid_filter( fname, strlen( fname ) ); if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: paranoia filter done. Filename='%s'\n", FL, fname ); return 0; } p3scan-2.3.2/ripmime-1.4.0.6/filename-filters.h0000644000175000001440000000034110347164530017407 0ustar jlaiusersint FNFILTER_init( void ); int FNFILTER_set_debug( int level ); int FNFILTER_set_verbose( int level ); int FNFILTER_set_paranoid( int level ); int FNFILTER_set_mac( int level ); int FNFILTER_filter( char *fname, int size ); p3scan-2.3.2/ripmime-1.4.0.6/CHANGELOG0000644000175000001440000017077610347164530015246 0ustar jlaiusersripMIME CHANGELOG Format of entry line: - : CHANGES--------------------------------------------------------------- Mon Dec 12 2005 - PLD:REL:1235 RELEASE 1.4.0.6 Updated ripOLE to version 0.2.0 Wed Mar 30 2005 - PLD:REL:0644 RELEASE 1.4.0.5 Fixed -d -i - combination error causing ripMIME to fail to decode Thanks to Daniel Fraga for noticing this. Tue Mar 29 2005 - PLD:REL:1703 RELEASE 1.4.0.4 Tue Mar 15 2005 - PLD:DEV:1545 OLE2 decoding fix (see ripOLE) Thu Feb 03 2005 - PLD:DEV:1950 Added recursion facility if the input mailpack/MIME file is a directory. Fri Dec 17 2004 - Release 1.4.0.3 - Improved attachment search abilities due to previous cleanups - Cleaned up excessively long header-search times - Cleaned up excess unwanted textfile outputs generated from the qmail-bounce searching. A reduction of ~25% has been attained. - Handled Exim bounced email attachments - TNEF segfault issues cleaned up (see TNEF's CHANGELOG) Tue Dec 07 2004 - PLD:DEV:0102 Fixed up Apple double content disposition INLINE issue which could cause ripMIME to try decoding a mailpack for a VERY long time. Thanks to Nicolas MacPherson for the offending mailpack. Wed Dec 01 2004 - PLD:DEV:2330 Fixed QP decoding error where trailing TABs and spaces were not culled. Mon Nov 29 2004 - PLD:DEV:1106 Fixed possible filename buffer overrun in MIME_decode_encoding() function where sprintf() was being used to generate the filenames rather than the correct snprintf() call. Sat Nov 27 2004 - PLD:DEV:2049 Added OLE2 decoding sanity fix (see ripOLE CHANGELOG) Fri Nov 26 2004 - PLD:REL:2238 Release as 1.4.0.2 - PLD:DEV:2028 Fixed up ISO decode issues in the headers causing premature truncation. Sat Nov 06 2004 - PLD:DEV:10H27 Fixed up numerous unsigned/signed comparison warnings. Fixed up TNEF read_32/_16 functions to allow for error condition returns. Fri Nov 05 2004 - PLD:DEV:22H57 Applied Stepan Kasal patches for MIME_decode_b64() 4-char array initization and for the boundary_crash test continue if statement. Wed Nov 03 2004 - PLD:DEV:09H48 Fixed compiler warning about MIMEH_save_doubleCR "comparison is always false". Tue Nov 02 2004 - PLD:DEV:10H41 Added handling for x-mac filename forward-slash separators. Sat Oct 23 2004 - PLD:REL:17H14 Release as 1.4.0.1 - PLD:DEV:16H52 Applied patch from Andreas Jobs to fix up situations where the multiple-filenames may not all be correctly hardlinked due to modifications being made via the FNFILTER_filter() calls. Many thanks to Andreas for picking up on this. Mon Oct 18 2004 - PLD:DEV:23H08 Updated ripOLE code to prevent segfault caused by negative sector values in OLE_follow_minichain() Sun Sep 26 2004 - PLD:DEV:17H27 Fixed UUENCODING detection in MIME_headers which would sometimes incorrectly choose UU rather than Quoted Printable. Thanks to Stepan Kasal for pointing this out. Sat Sep 25 2004 - PLD:DEV:21H24 Fixed memory leak where the header-info structure's string-stacks file filenames and names were not being flushed after use. Fixed accidental flagging of bad headers when a header set contained the same filename in both the content-type and content-disposition headers. Sat Sep 11 2004 - PLD:DEV:23H22 Fixed an endless loop in ole.c when dealing with zero length MINIfat reads. Tue Sep 07 2004 - PLD:DEV:21H10 Fixed UUENCODED decoding when the UUE filename is specified in the headers (see uuencode.c) Sun Sep 05 2004 - PLD:DEV:09H30 Fixed OLE_version() inclusion in MIME_version() when RIPOLE wasn't defined. Thanks to Tomasz Klim for noting this. Sat Sep 04 2004 - PLD:DEV:10H47 Fixed incorrect return-code propagation from lower recursion levels, causing ripMIME to prematurely terminate some decoding, thus not seeing some attachments. Thanks to Fred Van Andel for supplying the offending mailpack. Fri Sep 03 2004 - PLD:DEV:09H57 Fixed empty-boundary handling (Mailpack sent by Ian Samuel) Tue Aug 31 2004 - PLD:DEV:22H18 Fixed up a segfault which could occur while running with --debug on some emails (See bug #32) Mon Aug 30 2004 - PLD:DEV:20H21 Fixed up 'make solib' option (Thanks to Oden Eriksson for pointing out that it was broken) Tue Aug 17 2004 - PLD:REL:16H36 Released as 1.4.0.0 Mon Aug 16 2004 - PLD:DEV:13H43 Added strstack objects into the MIME headers struct (hinfo) to track multiple filenames exploits. When the filename has been saved to the disk, the additional filenames are then hardlinked (this prevents excessive disk consumption as hardlinks don't really take up space). Sun Aug 15 2004 - PLD:DEV:22H21 Added '--verbose-defects' which will dump all the header defects detected during the scanning of the MIME segment Decoding filename=attachment.exe Header Defect: Missing separators: 1 The number trailing the defect description is the number of occurances within the headers that the defect occurred. Detected defects won't stop the ripMIME decoding by default, instead ripMIME will step around the defects to the best of its abilities and flag the defect register in the hinfo defects[] array. Wed Aug 11 2004 - PLD:DEV:23H42 Added a new array 'defects' into the hinfo structure in MIME_headers.h. The purpose of this array is to track all the defects/mistakes in the MIME headers, allowing the calling program (of the MIME lib) to make a decision on the mailpack (ie, should defective mailpacks be allowed to pass through). More details will be added over the next few days. This new feature isn't quite ready yet. - PLD:DEV:23H22 Fixed handling of multiple sequential quotes on filenames and other parameters, ie; name=""attachment.exe"" - PLD:DEV:23H10 Added boundary pushing to the stack within MIME_headers in order to catch out situations where multiple boundaries are defined in the headers. - PLD:DEV:22H21 Changed ISO decoding routine to _not_ convert underscores to spaces. This is so that ISO encoded items such as boundary definitions aren't turned into non-valid items, ie __BOUNDARY__ becomes ' BOUNDARY '. Wed Aug 11 2004 - PLD:DEV:18H33 Added correct handling of RFC2231 encoded strings; such as: title*0*=us-ascii'en'This%20is%20even%20more%20 title*1*=%2A%2A%2Afun%2A%2A%2A%20 title*2="isn't it!" - PLD:DEV:17H10 Fixed up handling of multiple atom separators (=,: and ;) - PLD:DEV:12H57 Added RFC822 comment stripping from headers (while being processed in the MIME_headers module). The original headers will be preserved verbatim in _headers_ file. Mon Aug 09 2004 - PLD:DEV:14H48 Added --timeout feature which allows you to set a CPU seconds timeout. If the alarm goes off, ripMIME will terminate with an error code of RIPMIME_ERROR_TIMEOUT (5) and leave a log entry (if required). This feature is useful to prevent server-load runaway if ripMIME has a problem decoding a particular email - PLD:DEV:14H11 Added --formdata option to allow the decoding of HTML form data which often contains embedded \0 sequences which cannot be accurately handled by normal email-MIME decoding routines (yet) Fri Jul 30 2004 - PLD:REL:21H06 !!!!URGENT RELEASE!!!! Released 1.3.2.3 There's viruses going around exploiting the ability to hide the majority of their data in an attachment by using blank lines and other tricks to make scanning systems prematurely terminate their base64 decoding. This release fixes that by ignoring consecutive CR checks in the BASE64 data and rather attempting to treat it as a stream of data with arbitary invalid bytes in it. Thu Jul 29 2004 - PLD:DEV:23H11 Fixed infinite loop with OLE decoding in ripOLE, mailpack supplied by Dallas L. Engelken. The decoding is not correctly handled at this point (no sensible data can be extracted from the .doc file) but at least ripMIME/ripOLE will not infinately loop. Tue Jul 27 2004 - PLD:DEV:12H55 Applied filename paranoia patch provided by Marco Ariano Mon Jul 19 2004 - PLD:REL:12H04 Released 1.3.2.2 Sat Jul 17 2004 - PLD:DEV:14H22 Silenced unwanted verbosity from recursion bounds breach report in mime.c:2674 Silenced unwanted verbosity from tnef decoder about invalid signature when in non-verbose mode. Silenced unwanted verbosity from ripOLE (see ripOLE CHANGELOG) Sat Jul 03 2004 - PLD:DEV:14H18 libmime-decoders.c:MDECODE_decode_ISO() Fixed situation where ISO encoded string without a language page specification would cause the rest of the headers to be truncated from that point; typical example is: Subject: =??Q?Radiant=20Rubies=20Just=20for=20You.=20SAVE=20$$$'S.?= note the =?? rather than say =?iso-9352 etc Made the ISO decoder complain about a dud encoding type character if 'verbosity' is on, else, it doesn't report the error (this ensures that things like qmail etc don't get upset) Mon Jun 28 2004 - Release as 1.3.2.0 Sun Jun 27 2004 - PLD:DEV:15H31 Fixed duplication of 'Decoding Filename=' reporting when decoding a uuencoded MIME segment with the filename defined in the headers. - PLD:DEV:14H09 Added '--extended-errors' option to allow the turning on of non-fatal errors. Corrected various return-code issues #define RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR 1 #define RIPMIME_ERROR_CANT_OPEN_INPUT_FILE 2 #define RIPMIME_ERROR_NO_INPUT_FILE 3 #define RIPMIME_ERROR_INSUFFICIENT_PARAMETERS 4 - PLD:DEV:12H15 Fixed UUdecode situation where the MIME segment defines the filename in the headers, rather than the more common case of having a "BEGIN 666 filename" content. Thanks to Dallas L. Engelken for detecting this and supplying a suitable mailpack. Fri May 21 2004 - PLD:DEV:13H19 Fixed up boundary stack BS_cmp() size compairson check by creating a new function BS_is_long_enough() which takes the length of the line being tested and compares it to the current 'smallest permissable' boundary. Fixed special case boundary situation where the boundary is defined only as '-'. Thu May 13 2004 - PLD:REL:14H07 Release 1.3.1.2 - PLD:DEV:00H15 Due to the MIME headers alteration, the ISO decoding for filenames started having issues because of gaps in the decoded ISO string. This has now been fixed. Wed May 12 2004 - PLD:DEV:21H57 MIME_headers.c:1295; added \t as another possible header line delimiter (along with \n \r and ;) - PLD:DEV:21H55 Corrected the header unwrapping according to RFC2822 specifications. See comments in MIME_headers.c ~ line 886 Mon Apr 12 2004 - PLD:DEV:21H12 Fixed up some compile errors on Solaris 9 (char indexes) Mon Apr 5 2004 - PLD:REL:19H38 Released as 1.3.1.1 - PLD:DEV:11H28 Fixed TNEF LOGGER call with missing paramter Fixed possible underrun in the MIMEH_read_headers() call when trimming off trailing \r and \n chars from the read header line. Fixed some LOGGING lines which were not correctly setup with the MIMEH_DEBUG test (resulting in spurilous debugging info when it wasn't desired) Added 'smallest stored boundary' (glb.smallest_length) to the boundary-stack module so that it could quickly eliminate a lot of boundary tests based purely on the size of the input test line. This also eliminated the problem that would sometimes appear in valgrind where an error in BS_cmp() with the strcmp() call would arise. The smallest_length variable can only decriment. Theoretically it could be reduced to a point where it's value is '1' but it won't affect the selectivity of the algorithm. The worst case scenario is that smallest_length is set to 1 in which case the algorithm will behave as before the change in the code was made. - PLD:DEV:01H59 Added header subject initializer into MIME_headers_get() to stop valgrind complaning on certain emails where the subject would not be located in the headers. Added more stringent testing of the subject copy-over process in MIME_unpack_stage2 after the headers get call. Sat Apr 4 2004 - PLD:DEV:23H54 Corrected a segfault when running debug mode and mime.c:2623 logger line was called (didn't have a ,FL at the end) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_plain:DEBUG: Handling plain email",FL); Explicitly called OLE_decode_file_done() after the OLE decode process because sometimes files weren't being closed due to a non-OLE file being opened (hence closing premature of the internal call of _file_done(). It's okay to call this function multiple times as it checks all the variables to ensure that they're not NULL before trying to clean them up. Thu Mar 11 2004 - PLD:DEV:12H44 Updated man page for 1.3.1.x Wed Mar 10 2004 - PLD:REL:23H59 Re-released 1.3.1.0 - PLD:DEV:23H59 Switched all calls (interal) to MIME_unpack() to MIME_unpack_single(), especially for the mailbox handling routine - which would cause the whole system to loop and fail. - PLD:REL:08H31 Corrected generate-buildcodes.sh where the date stamp might not return a valid integer. Tue Mar 9 2004 - PLD:REL:01H15 Release 1.3.1.0 Set --qmail-bounce option to be on by default (appears to be quite stable) and added --disable-qmail-bounce to turn it off instead. Sun Mar 07 2004 - PLD:DEV:15H30 Dev release. - PLD:DEV:13H20 Altered UUEncode's error reporting system. Now when a function returns -1, the calling program should check the global uuencode_error to see what the error is. This system is similar to those used with a lot of standard functions like fopen(). The error codes are listed in uuencode.h We needed to do this so that we could return positive-integer error codes from UUENCODE_decode_uu(), because normally this function will return a positive integer representing the number of files it decoded from the supplied file. - PLD:DEV:12H50 Corrected mistake with the recursion level passing in MIME_unpack_single_fp() which was passing '0' value to MIME_unpack_stage2(), causing ripMIME to fail with a segfault when very deeply recursed emails were encountered. NOTE this segfault occurs because of running out of stackspace, not because ripMIME specifically is broken. ripMIME seems to be able to recurse to about 160 levels deep on the test machine before stack failure. - PLD:DEV: Set MIME_set_header_longsearch() to only take effect if we're calling a plain-text file, ie, being handled by MIME_handle_plain() Sat Mar 06 2004 - PLD:DEV: Unbelievable amount of restructuring required so I could get 2 mailpacks with seperate problems to work. Improved the discrimination and speed of the BS_cmp() operation by first taking a count of all the numeric+alpha chars in the boundary and then comparing against the existing boundaries. This helps speed things up because there's no longer a string comparison per test if there's not even going to be a chance of a match. Also, the discrimination is improved because I had an incident where two boundaries existed, with one being a substring of the other, this was passing through ripMIME with the previous 1.3-dev release for some reason. A lot of code was removed from the MIME_unpack_stage2() call and placed rather into seperate function calls. The new function calls are; MIME_handle_multipart() MIME_handle_rfc822() MIME_handle_plain() No doubt more of these will be added over time as I work my way through the old code. Fri Mar 05 2004 - PLD:DEV: Added a stack system for storing the filenames used during any given MIME_unpack() call, this means that when the MIME_unpack() call finishes, it doesn't clobber the textfile* files which might have actually been owned by a parent process (as MIME_unpack() is recursive in some situations). This addition once more beefs up the size of ripMIME, but is required in order to deal with the ever increasing complexity of emails which are passing through our systems. The new files are strstack.c strstack.h These two new files are actually adapted/stripped-down versions of the boundarystack files. - PLD:DEV: Added a new script to the build process to generate a file called 'buildcodes.h'; this file contains the date in UNIX timestamp and human-readable date, along with the output from 'uname -a' This has been added to assist mostly in local build checking, but also may serve as a good indicator on other machines to see if a ripMIME version is absolutely current. Mon Mar 01 2004 - PLD:DEV: Corrected misspelling of 'QuotePrintable' to 'Quoted-Printable' - PLD:DEV: Updated ripOLE licence to match the ripMIME licence. - PLD:DEV: Altered the UUDECODING routine to not complain about lack of permissions on a line which starts with begin but is perhaps from a plain-text file (ie, not UUENCODED). Suggestion given by Daniel Fraga. - PLD:DEV: Removed --syslog_on and --stderr_off paramters from the help dump (thanks to Mat Williams for noticing this). These paramters have been deprecated for quite a long time. Mon Feb 16 2004 - PLD:DEV: Added ripmime.1 man file Sun Feb 08 2004 - PLD:DEV: Adding facility to pull out/decode qmail bounced emails - this is against RFC compliance, but people are wanting this badly so I suppose, once again, Microsoft wins (because Outlook seems to gleefully find the attachments when they should in fact be displayed as verbatim - not decoded). use the --qmail-bounce parameter to activate this 'feature'. - PLD:DEV: Rolled in a patch by James Stevens to make ripMIME usable for his AV milter tool. Also included a patch to (for now) split the MIME_unpack_single() into _single_fp() and _single(), paving the way for future file-handle based processing rather than FILE * - PLD:DEV: Added recursion-maximum level paramter (--recursion-max) as suggested and patch-supplied by James Stevens (some alterations made to the patch) Wed Jan 14 2004 - PLD:DEV: Added RCF822 content checking for files generated by the text dump from an UNSPECIFIED encoding/type format. This allows us to pick out mailpack contents even when there's a set of false headers prior to the real body content (as depicted by the samples provided by Farit - many thanks ). Thu Dec 18 2003 - PLD:DEV: Updated licence file to correctly indicate the true nature of this project Sun Dec 07 2003 - PLD:DEV: Completed the 'quiet' functionality - PLD:DEV: Fixed up decoding of multiple segment filenames in headers, ie; filename*1="foo"; filename*2="bar."; filename*3="exe" - PLD:DEV: Corrected handling of */octet content type storage, opting to use RAW decoding rather than plain text. Sat Nov 14 2003 - PLD:REL: Released ripMIME v1.3.0.5 Fri Oct 30 2003 - PLD:DEV: Fixed up decoding issue in stage2 where ripMIME would try to decode a zero-byte file produced from the BASE64 decoder routine. Fri Oct 23 2003 - PLD:DEV: Added integration of the -p flag with the --name-by-type flag. The prefix will only appear on the type-based named attachments if it's explicitly requested (ie, it will not appear if you don't use the -p flag, even though there is a default prefix ). Thu Oct 23 2003 - PLD:DEV: Added --name-by-type feature (as requested by Marcel Manz) to create the filename of an attachment if it has no filename= or name= header based on its content-type Fri Oct 10 2003 - PLD:DEV: Moved house - completely changed everything in my life. Haven't changed much in ripMIME. Fixed some Makefile issues and incorrectly included .o files, as reported by Paul Theodoropoulos (thanks!) Wed Sep 10 2003 - PLD:DEV: Rolled in changes from valgrind process. Fixes several potenial segfault situations, especially with ripOLE. Fri Aug 29 2003 - PLD:DEV: Fixed mailbox decoding from STDIN Thu Aug 28 2003 - PLD:DEV: Changed the mime-headers output path setting to be set from within the MIME_unpack() call, rather than allowing it to be set externally Prior to this change, doubleCR files were being saved in places like the root directory and other unsavory locations. ripOLE library updated - segfault fixed when reading miniFAT with a negative start sector value Sun Aug 24 2003 - PLD:DEV: Fixed up situations where an embedded email with an attachment filename (As apposed to just raw embedded with RFC822 content-type) could not be decoded (would be passed over and ignored). Thanks to Ricardo Kleemann for supplying a sample mailpack. On fixing up for this one mailpack, my test suite jumped from decoding 1092 attachments to 1193 attachments. Wed Jul 16 2003 - PLD:DEV: LOGGER % filtering solution fixed - PLD:DEV: Bundled in the ripOLE engine into ripMIME, this means that ripMIME can now extract files which have been included in an MS Office file. Currently ripOLE cannot extract attachments which have been embedded directly into the WordDocument stream data however (this is usually Images and some sound files). However it does successfully get most/any files which were included using Insert->File Added --no-ole option to make ripMIME not decode OLE files. Mon Jun 30 2003 - PLD:DEV: Added libripmime build ability, allowing developers to use #include "ripmime-api.h" and then link with libripmime. Sun Jun 22 2003 - PLD:DEV: Fixed QuotedPrintable end-of-string decoding 1-off char error which would cause ripMIME to complain that it ran out of data because it was comparing "if (ip < slen -2)" when it should have been comparing to slen -1 (because the '<' implies a -1 by default ) Thanks to Paul Theodoropoulos for his submissions of mailpacks which have greatly assisted in locating these obscure errors. Sat Jun 21 2003 - PLD:DEV: Fixed UUdecode false-positive hit in UUDECODE_is_uuencode_header() (thanks to Bernard Fischer once more for supplying a mailpack which demonstrated the fault and for providing a patch ) - PLD:DEV: Added an explicit RFC822 decoding routine into MIME_unpack_stage2() because a supplied mailpack which had multiple documents, all in individually wrapped RFC822 attachments caused ripMIME to incorrectly interpret the segment, till the end of the mailpack as the entire RFC822 message ( consequently, the recusion limit would eventually kick in and exit ripMIME ). By placing an explicit detection into _unpack_stage2(), the problem has been eliminated without affecting the decoding accuracy of other mailpacks. Wed Jun 18 2003 - PLD:DEV: Relaxed the base64 decoding standard to allow for a possible single blank line within the B64 stream. This has been needed because there are some rare mailpacks produced by [obviously] broken agents which have spurilous blank lines amongs the data-stream ( but are terminated by boundaries ) I feel this is a really ugly way to deal with this problem - people should really make their B64 encoders conform to realistic standards rather than hoping that all the decoders will pick this up accurately I would have left out the CR sensitivity entirely - except that there is another example mailpack I have where [incorrectly] there is plain text after the B64 stream has ended ( incorrectly inserted disclaimer no doubt ). I may revoke this relaxation if it can be found that the 'other' example mailpack is in fact interpreted [wrongly] by all other MUA's. Update - both mailpacks are correctly decoded by Sylpheed :-\ Sun Jun 15 2003 - PLD:REL: Released 1.3.0.4 - PLD:DEV: Corrected UUdecoder path issues, causing UUencoded attachments to sometimes be missed from decoding Sat Jun 07 2003 - PLD: Corrected misinterpretation of the return result from uuencoded_decode where mime.c was propergating the return result into the _decode_stage2() calls, causing processing of the mailpack to prematurely halt. Thanks to Jiri Kucik for submitting the offending mailpack. Fri May 09 2003 - PLD: Attempting to clean up the handling of % symbols in filenames and their output via the LOGGER_log() call. Normally they used to be converted into %% pairs so that the final printf type call would not try and interpret the % as a positional identifier. ---still in work. Tue May 05 2003 - PLD: Added NULL test for filename reporting callback in mime.c Without this NULL test, other applications using the mime module could fail if they did not have the callback function set to their own. Sat May 03 2003 - PLD: Added Content-Transfer-Encoding 'binary' to ripMIME, which causes ripMIME to interpret the data the same as 'raw'. Fri May 02 2003 - PLD: Added --no-mht switch, disables the decoding of MHT files (A microsoft 'combined' WWW-page-in-email sort of format) Wed Apr 30 2003 - PLD: X-men 2 at theaters - PLD: Added option-usage for --no-tnef - PLD: Enhanced pldstr module to provide string search/replace functionality and case-insensitive strstr Sun Apr 27 2003 - PLD: Pushed global variables into singular global struct for boundary-stack module - PLD: Optimised the boundary-stack BS_cmp() call so that it will only do strcmp calls (rather than strstr()) for the first N characters, where N is defined by BS_BOUNDARY_DETECT_LIMIT_DEFAULT and consequently, BS_set_boundary_detect_limit(). Currently the default is set to 4 characters, I do not know of any mailpacks out there which are using more than the 'defacto' '--' prefix to the boundary to seperate MIME sections - PLD: Added extra parameter to the BS node, to store the length of the boundary, saves processing time later when comparing boundaries in BS_cmp() - PLD: Corrected DoubleCR decoding issue in UUENCODE_decode_uu() - PLD: Fixed ranging error in filename renaming method selector (MIME_set_renamemethod) - PLD: Activated the --no-quotedprintable option. This will stop ripMIME from attempting to decode/decypher attachments which have been QuotedPrintable encoded (some forwarded emails are encoded like this ) - PLD: Added a small cleanup function for after BASE64 decoding. Normally when BASE64 decoding finishes there's often a single line left before the next boundary, this typically has been written out as a textfileXX file. The additional function absorbs this data [and discards]. This also should improve [ a little ] the performance as ripMIME will not have to go through the entire header-reading/parsing/ decoding process each time. Sat Apr 26 2003 - PLD: Dropped a lot of old code which was residing in MIME_headers for the sake of 'rollback' in the event that the new header parsing code didn't work. Sat Apr 19 2002 - PLD: Added fixes for VCARD/UUENCODE detections thanks to Bernard Fischer. Sat Mar 29 2003 - PLD: Added better handling of the apple-double encoding, now when and attachment is decoded, it'll be decoded into 3 parts (overall) 1. mac-FILENAME : This usually contains nothing and can be ignored. The file is saved in the event [for what ever reason] that data or text was placed at the start of the apple-double attachment pair [ Similar in a way to the potential presence of data at the beginning of a MIME encoded email, which usually says "This email is encoded with MIME". 2. FILENAME.applemeta : This is the metadata associated with the original attachment 3. FILENAME : This is the actual file/attachment Fri Mar 28 2003 - PLD: Release - PLD: Replaced header parsing code with one which follows the 'standards' compliant structures ( while still looking out for various exploits ). This new parsing code will open the way for better information passing [back] to programs using the ripMIME API. Also, the new header code improves the false-positive rate with filenames. - PLD: More intelligent support for the Macintosh Applefile format, which uses a two 'file' approach for sending files ( meta data file and data file ). Now, any mac files will have their applefile-metadata stored as 'applefile_*'. Although this is not yet perfect, it at least ensures that the real data file is correctly saved with the right name. Thu Mar 20 2003 - PLD: More typos in the LOGGER_log() calls fixed up - PLD: Removed the functions MIME_set_nouudecode() and UUENCODE_set_nodecode() because they were 'confusing' due to inverted logic, additionally it brings the functions inline with all the other modules which use _set_decode() to [en/dis]able the decoding process. Wed Mar 19 2003 - PLD: Corrected several search-replace messups from original conversion to running everything under LOGGER. - PLD: Added facility to report filename decodings from within the various decoding modules (TNEF and UUENCODE at this point) so that the reporting function can be defined from the calling application. If no callback is provided, the modules will default back to a very basic 1.2.x format. Mon Mar 17 2003 - PLD: Emergency release of 1.3.0.2 - PLD: Fixed major bug in the MIMEH_headers_read() call which would cause the first line of the headers to be overwritten by following lines ( in memory ) - Ultimate effect of this is that several files would not be decoded because the vital file information would be lost. - PLD: Unified logging output across all modules - PLD: Fixed space retention on header-wrapped filenames ( again thanks to Kees Damen for providing a suitable mailpack ) Fri Mar 14 2003 - PLD: Fixed linewrap filename issues in headers ( thanks to Kees Damen for pointing this issue out ). - PLD: Fixed 0 length string issue with PLD_strncpy and replaced existing algorithm with a more robust one as provide by 'Defrost' - PLD: Rolled in unified pldstr module which replaces strlower, XAM_strtok and zstr. Sat Mar 01 2003 - PLD: Patched parameter parsing code to allow it to accept non-space seperated parameters, ie, -d/tmp ( Patch supplied by Bernard Fischer ) Sat Feb 22 2003 - PLD: TNEF: Corrected pointer clobbering on 64-bit systems in TNEF decoding, and added memory allocation free'ing. Both patches submitted by Bernard Fischer and adapted by PLD Thu Feb 19 2003 - PLD: Fixed content-type 'trailing' issue causing ripMIME to report the previous content-type if the new attachment headers did not contain a content-type. ( Mailpack contributed by Bernard Fischer ) Wed Feb 19 2003 - PLD: Converted command-line options to use hyphens '-' rather than underscores '_' for word seperators. NOTE, the old underscore seperators will still work but they are considered depricated. - PLD: Added multiple additional verbosity options: --verbose-oldstyle : Output information in the format used from the 1.2.x series of ripMIME --verbose-contenttype : Output content-type information of each file decoded ( Contributed by Thomas E Dell ) - PLD: Introduced a new verbose information format for file details. The new format uses name=value pairings for the output, this allows parsers to easially decode the output into useful data if need be. An example of the output is now: Decoding content-type=text/html filename=foobar.html Currently only 'content-type' and 'filename' are reported but more options will most likely be added in the future. - PLD: Commenced v1.3-dev development. Fri Feb 07 2003 - PLD: Released as 1.2.17.0 ( This is the last 1.2.x release, we will now move to 1.3-dev and 1.4-stable releases Thu Feb 06 2003 - PLD: Corrected testing conditions of the name= substring to test for both a space OR a ';' (semicolon) in order to accept the value after the 'name=' as being a filename. (Thanks to Marc Bleuwart for the supplied mailpack exhibiting this issue ) Wed Feb 05 2003 - PLD: Added total BASE64 decode line count output for failed decoding operations when in --debug mode Sat Feb 01 2003 - PLD: Corrected some *_init() calls and *_set_verbose() as well as *_set_debug(). - PLD: Broke down the mime.c file into some smaller modules: boundary-stack.[ch] - Handles the boundary string ordering for the mailpack decoding. filename-filter.[ch] - Performs basic filename sanity / paranoia checking. uuencode.[ch] - A bit of a misnomer, this module currently deals with the detection and decoding of uuencoded data. - PLD: Merged 3 string handling modules into one. While this seems to go against the flow against the move to modularise the mime.c file it is better to have the files XAM_strtok, zstr and strlower all merged as they are all part of the same family of strings handlers. - PLD: Moved ISO decoding to directly after all the headers have been read and a "true blank" has been received. This way we do not have to worry about decoding the individual components of the headers, rather they are presented in an already decoded form when the calling program gets back the header pointer. Fri Jan 31 2003 - PLD: Reordered the MIME_decode_filename() function so that it attempts to decode ISO filenames -before- any paranoia filters are applied. This is because certain BASE64 characters ( ie / ) are interepreted as paranoid invoking characters, thus are stripped from the filename before ISO decode gets to use them. Thanks to Michal Perchel for supplying the mailpack exhibiting this property. Tue Jan 28 2003 - PLD: Replaced MIME_read() fgetc/fputc calls with a fread/fwrite binary functions to prevent ripMIME from terminating its file/stdin reading too soon if it received an EOF character. Mon Jan 27 2003 - PLD: Corrected segfault in parameter parsing if the last parameter was a -d, -i or -p. Whole parameter parsing block moved to its own function. - PLD: Replaced scandir() calls in mime.c ( for post-decode cleanup ) with opendir()/readdir() - Solaris ( and possibly other OS's ) have different implementations of scandir() compared to Linux/BSD - PLD: Silenced several messages in MIME_headers and MIME tnef decoding which were appearing despite having -v selected Sun Jan 26 2003 - PLD: in MIME_decode_filename(), removed limitation of filenames only having a space character and lengths greater than two (2). - PLD: Corrected header unfolding in MIMEH_read_headers() This rectifies the situation where some filenames are cropped because they were spread over two lines in the headers. - PLD: Moved ISO decoding functions and QuotedPrintable functions into a new module called 'libmime-decoders'. - PLD: Initialised encoding_type in the ISO decoding routines in order to silence some compilers about a 'usage unitilised' warning. - PLD: Corrected situation where an attempt to extract a file name would be misled due to a substring 'name' appearing anywhere within headers. Narrowed down the conditions required in order for a 'name' word to appear. This corrects the issue as reported by Chris Hine - PLD: I've sorted out the Quoted-Printable decoding issues. Who would have thought such a relatively simple decoding process could cause so much pain. As a consequence some of the prior 'patches' have now been removed as they are no longer needed. ( removed patches: Mirko Buffoni ) Sat Jan 25 2003 - Planning on releasing as 1.2.17.0 Fri Jan 24 2003 - Created a new function to handled decoding of ISO encoded strings, now called MIME_decode_ISO(). This function is used by MIME_decode_filename() and will also be used by any programs wanting to decode other ISO encoded strings such as email addresses and such. - Removed MIME_init_hexconv() call ( which previously setup the BASE64 conversion matrix - but we now have a compile-time static array setup for that ) - Renamed 'quick_clean_filename()' to be MIME_filename_paranoid_filter() which is called from within MIME_decode_filename() - Renamed MIME_clean_MIME_filename() to MIME_decode_filename() as its primary function is mostly to handle ISO decoding as well as path prefix stripping. - Deprecated --no_paranoid and rather added in --paranoid. Setting --paranoid on will result in any high-ascii characters to be set to _'s - Changed void return value of quick_clean_filename to 'int' - Added testing of '=' char prior to a ? for testing presence of an ISO encoded filename in MIME_clean_MIME_filename() - Added functional handling of ISO encoded filenames in headers ( see MIME_clean_MIME_filename() ). Still need to merge the functions of MIME_clean_MIME_filename() and quick_clean_filename() Wed Jan 22 2003 - Corrected functioning of --no_nameless operations. Tue Jan 21 2003 - Added trailing \n break after decoding a QuotedPrintable line (submitted by Mirko Buffoni ) Mon Dec 16 2002 - Corrected soft-break decoding of Quoted-Printable encoded data Tue Nov 12 2002 - Released as 1.2.16.21 - Updated FFGET module to 1.0.0.3 release - fixes garbage-end problem with some BASE64 decodes which don't terminate their data with a \n[\r] Sat Nov 09 2002 - NOT-RELEASED (internal use only, see 1.2.16.21) 1.2.16.20 - Changed the strlower() function to use unsigned char rather than signed char, this allows for non-ASCII conversions on the tolower() call without breaking (ie on non-english character sets) Wed Nov 06 2002 - Released as 1.2.16.19 - Added a patch from Phil Brutsche to deal with space-embedded filename, name MIME fields. Fri Oct 11 2002 - Corrected segfault when a filename with a \" ending was encountered - Cropped filenames with ? in them which are NOT ISO encoded to only include the part before the ? Thu Oct 10 2002 - Updated the logger file which contained a segfault issue when sending a string containing a % symbol to syslog. - Wed Jun 26 2002 - Added MIME-headers save-file closing function. This prevents a segfault when MIME_unpack() is called multiple times within the same program. Fri May 31 2002 - Release as 1.2.16.16 - Fixed same issue for Double-CR saved files - Failure to prefix the unpackdir onto the recursive decoding of mailpacks extracted from within mailpacks (ie, EML files) caused extractions to directories other than the present-working-dir to fail [partially] due to not being able to locate the newly created / extrated mailpack. That teaches me to 'ignore' the warnings that were popping up in my syslogs about 'Cannot locate file'. Sun May 26 2002 - Release as 1.2.16.15 - bug causing ripMIME to enter into an infinite loop if the boundary contained the phrase "boundary=" within it. Thanks to Steffen Clausjuergens for supplying the mailpack exhibiting this defect! Sat May 25 2002 - Release as 1.2.16.14 - Minor bug in FFGET_raw() causing missing of decoding due in mailpacks which have mixed EOL definitions, ie, the email uses \n only in the main headers, but then switches to \r\n in the bodies [ Can someone /PLEASE/ tell me why any peice of email software would do this !?!? ] Wed May 22 2002 - Release as 1.2.16.13 - Corrected block-boundary read line error with FFGET_fgets() - Corrected segfault error with UUDECODE due to XAM_strtok() Sun May 12 2002 (Mothers Day - Be nice to your mother and take the day off from IRC'ing or sufing the net) - Release 1.2.16.12 .... one day I'll release 1.2.17.0 - Improved nested decoding. There was a very strange size-specific decoding mishap which was causing some packs to unpack flawlessly, others to fail, with the only difference being that one had more than 1089 bytes of headers. Cleaning up the memory leak issues resolved it. Though I'm always a bit fearful of such dissapearing bugs. - Cleaned up memory free'ing, used 'ccmalloc', nice little tool I recommend it to people trying to at least get a coarse view of what is being left out. I found that simply by fixing up the memory leaks I had, one of my "wierd" bugs went away very nice of it. Tue May 07 2002 pldaniels@pldaniels.com - Added new function to MIME_headers object, to return the current status of the headers save status. This allows us to prevent opening up multiple FILE *'s on the same named _header_ file, potentially causing corruption - Removed the "WARNING: Possible pointer size being sent" debugging message. Tue Apr 30 2002 pldaniels@pldaniels.com - Updated TNEF with patch supplied by Joe Gooch (Thanks Joe) corrects segfault when decoding certain TNEF's. - Added new function in FFGET. FFGET_set_watch_SDL() tells FFGET to either be watching out for ^M^M sequence, or not to (1, 0 respectively). This is required so that we can prevent ripMIME from interpreting the ^M^M sequence if it's found in the email BODY as a SDL-Outlook exploit. Thanks to Elio de Santi for the mailpack which revealed this Fri Apr 26 2002 pldaniels@pldaniels.com - Lots of fixes in the FFGET routines. Including the introduction of a two-stage FEOF status. Initially we mark that we've reached the end of the /real/ file by flagging FILEEND, then, when we actually try to read a new data block using FFGET_getnewblock() we flag FFEOF. Previously we'd flag FEOF too early. This would cause corruption of various attachments. Tue Apr 23 2002 pldaniels@pldaniels.com - corrected memory leak causing "Killed" messages on large mailpacks with lots of text/print-quotable attachments Mon Apr 22 2002 pldaniels@pldaniels.com - Re-tuned the UUencode line detection sequence. If anyone is aware of a /precise/ method to detect a uuencoded attachment in mailpacks, both as RFC and Outlook[stupid], please email me on pldaniels@pldaniels.com Fri Apr 19 2002 pldaniels@pldaniels.com - Corrected FFGET_fgets() function which was returning NULL one call too early. Rather than sending back the last "partial" line read (end of buffer), it would assign the data to the buffer, but also return NULL, which caused some programs to exit early from their reading loops. I'm actually quite surprised things even worked with this error... amazing what can slip by. Sun Apr 14 2002 pldaniels@pldaniels.com - Corrected _headers_ file output location, which was defaulting to the execution dir, rather than the unpackdir Fri Mar 08 2002 pldaniels@pldaniels.com --Released as 1.2.16.6 - Added ability to decode "x-uuencode" files even though this type technically is not specified by the RFC's (one should be using BASE64 or QuotedPrintable. - Corrected UUencode filename extraction from the header triple Thu Mar 07 2002 pldaniels@pldaniels.com --Released as 1.2.16.5 - Fixed XAM_strtok() call which was occasionally failing if there was a delimeter at the end of the string. Wed Mar 06 2002 pldaniels@pldaniels.com - Renamed zstrncpy.[ch] as zstr.[ch] as it's becoming a generic library with more than just the zstrncpy function in it. It now contains the zstrncat and zstrncate calls Tue Mar 05 2002 pldaniels@pldaniels.com --Released as 1.2.16.4 - Replaced all strncpy and strcpy routines with zstrncpy() calls. Sun Mar 03 2002 pldaniels@pldaniels.com --Released as 1.2.16.3 - Replaced file write routines in BASE64 and UUDEC decoders so that they use buffered write (reduces the number of fwrite() calls. This also gained another 10% of performance over direct write. Sat Mar 02 2002 pldaniels@pldaniels.com - Replaced in MIME_base64_decode() the FFGET_fgetc() call with a direct source code from the FFGET_fgetc() routine. Whilst this does mean that I will have to keep track of the changes I make in FFGET_fgetc and ensure that I port them across to the BASE64 decode routine, it does make for a good 10% gain in performance as shown on a 80Mb data test suite. - Converted Base64, Hexconv and UUDEC routines into static (compile time) fixed tables so that there isn't any need to build them each time the program is run. This saves us on startup time, although minimal, it does help. Thu Feb 28 2002 pldaniels@pldaniels.com - Released as 1.2.16.2 - Updated FFGET routines with patch as supplied by J.Gooch Tue Feb 26 2002 pldaniels@pldaniels.com - Moved to 4 digit version numbers - Added f->FFEOF = 0 to the ffget_set_stream function. Was causing an intermittent FEOF() status based on random data without it being there. Sat Feb 23 2002 pldaniels2pldaniels.com - Committed ripMIME to local CVS Wed Feb 20 2002 pldaniels@pldaniels.com - Releasing as v1.2.16 - Cleaned up FFGET routines and added more comments. - Added new DEBUG mode selection routines. Now debugging can be activated on a per-level basis, with (default) now there being *_DNORMAL == Debug normal *_DPEDANTIC == Pedantic debugging (lots of output) ie, in mime.c, there is now lines like if (MIME_DPEDANTIC) fprintf(stdout,"%s",line); This replaces the existing non-level sensitive routines of if (_MIME_debug) .... - HUGE stuff up. Non-quotedprintable encoded text files were not being written out because I accidently left out the fprintf statement in MIME_decode_text() routine... REALLY bad MISTAKE. This was causing text files to be written out as 0-byte files. Fortuntely this error was picked up by the Xamime testing suite. - Releasing as v1.2.15 (I wonder if these releases will stop coming out so quickly) - _FINALLY_ fixed TNEF decoding, many thanks to Chea Chee Keong for the help provided, very valuable. - Releasing as v1.2.14 - FEOF detection mishandling in MIME_decode_uu() cleaned up (uses the FFGET_feof() call rather than feof(), which (the feof) could prematurely return TRUE rather than when FFGET returns true, this is due to the fact that the FFGET routines will (on the last valid read) read up to the end of the file causing feof() to return TRUE, however, until all the data is read out of the FFGET buffer, it should not return true. Hence, all routines using feof() were replaced with FFGET_feof() Mon Feb 18 2002 pldaniels@pldaniels.com - Releasing as v1.2.13 - CRCR logic cleaned up in FFGET_fgets() (thanks to Joseph Gooch for his help in resolving the logic routines) - Initialisation of ungetc flag in FFGET - Removed MIME detection debugging output - Added anti \0 poisoning filtering Sun Feb 17 2002 pldaniels@pldaniels.com - Releasing as v1.2.12 Although I've not had a chance to verify that ripMIME extracts all the possible \r explioits which can be used against Outlook/Express I am certainly confident it can extract most of them. I've ran this ripMIME against about 600Mb of normal emails, all of which have extracted correctly in regression tests. I'm _STILL_ seeking more assistance to deal with TNEF decoding. I have one mail pack which refuses to correctly extract, rather it results in the TNEF routines attempting to access data beyond the limits of the file (yes, it's protected against that, but it's still annoying!) - Added FFGET_fgets() true blank detection for reads, this is to deal with potential trickery as mentioned in bugtraq regarding fgets() falsely returning a single \n as though it was a blank line when really, it was just the previous fgets didn't have enough buffer space to include the last \n. - Corrected file Unique Nameing name cropping. Fri Feb 15 2002 pldaniels@pldaniels.com - Made ripMIME report its version via STDOUT instead of STDERR - Changed "Decoding TNEF format" report to report to STDOUT instead of STDERR - Replaced entire file I/O routines to use FFGET, which now supports fgets(), fgetc() and raw-get routines. This was done so that we could (originally) detect the presence of an offending double byte in the headers of some emails which may be used to exploit Outlook's interpretation. - Fixed zstrncpy which was copying one byte too few when a strncpy was done. This was causing filename crop problems. -NOTE- this was primarily due to the fact that the size of buffer specified was inclusive of the \0 terminator.... as apposed to an actual BUG - Added into MIME_headers the ability to decode "Content-Location" to extract filenames from the new Microsoft MHF formatted emails (basically it's a bundled together WWW page HTML + images) Thu Feb 14 2002 pldaniels@pldaniels.com - Added header detection of double CR exploit usable against MS Outlook (This feature is more of use to programs which actively filter against exploits such as Inflex/Xamime > Need more help with TNEF decoding!!!!! Wed Jan 30 2002 pldaniels@pldaniels.com - v1.2.11 - Added ability to decode MULTIPLE UUEncoded attachments within the same body of text. Tue Jan 29 2002 pldaniels@pldaniels.com - v1.2.10 - Corrected mistaken use of 'n' count in UUdecode byte count resulting in excessive sized files (with useless data) - v1.2.9 released - Removed non-used functions MIME_getchar() and MIME_insert_X_header() - Added UUDecode routines - Added --no_uudecode option Mon Jan 28 2002 pldaniels@pldaniels.com - v1.2.8 released - Patch suppled by Tim J. Robbins for buffer-smash as specified by BugTraq applied Thu Nov 15 2001 pldaniels@pldaniels.com - Corrected potential buffer overrun situation in raw-decode mode - Corrected buffer overrun with MIME_headers which allowed massive sized filenames to cause segfaults (Thanks to Tristan Aston for bringing this to my attention) - Various code cleanups and extentions to the mime.[ch] files to deal with new advances in Xamime (http://xamime.com) Fri Nov 09 2001 pldaniels@pldaniels.com - Replaced several sprintf()'s with snprintf - Set a global _MIME_STRLEN_MAX length to 1023 and used with snprintf() to avoid buffer-overflows as reported by Tristan Aston (Solaris 8). - Mon Nov 05 2001 pldaniels@pldaniels.com - Resolved TNEF segfault issue with some winmail.dat packs which do not seem to conform to the standard which we have for the current tnef decoding module. Some winmail.dat packs still dont unpack but that's an issue with the MAPI data I believe (anyone want to help?) - Added "RAW" format for encoding, RAW basically is a slower form of read/decode, but, it will at least correctly (??) save non-format specified files. RAW decoding routines are kept in rawget.[ch] which is nothing more than a specific version of fgets(). This may change later [back to fgets()]. The difference is that rawget returns how many bytes it read, because the data returned by a binary file may contain \0's which would otherwise fool length testing function calls (ie, strlen()) into indicating there are less bytes than what there really is. This could be resolved by performing a test for the \n or \r, but it's more overhead. - Added patch from Xerox to correct occasional issue with non constant BASE64 line lengths (previously used to determine when the encoding had finished) Fri Oct 26 2001 pldaniels@pldaniels.com - 1.2.5 released - Removed mailbox "\n\rFrom " line detection routines out of text decoding section, created new function MIME_unpackmailbox() which will break apart the mailbox email at at time and feed it to MIME_unpack() - [mime.c, tnef.h, tnef.c ] Fixed issue with TNEF decoding segfaulting if -d param was used in ripMIME - [config.h] Cleaned up the __BYTE_ORDER mangling issue by renaming as __TNEF_BYTE_ORDER - Added numerous debugging messages available by using --debug on ripMIME - [tnef.c] Removed extensive deadcode from tnef modules - [tnef.c] Removed compressed RTF decoding - Improved error reporting and messages Thu Oct 18 2001 pldaniels@pldaniels.com - 1.2.4 released - Added TNEF decoding ability through use of modified tnef2txt library as created by Brandon Long (blong@uiuc.edu), April 1997. As yet, I have not been able to successfully contact this person. Tue Oct 09 2001 pldaniels@pldaniels.com - Updated MIME Boundary stack "cmp" operation so that it pops off any boundaries -above- the detected one. This is because technically, all boundaries should be nested in their occurances. This is a hoped fix for most of the .eml attachment extraction problems that were occuring. Tue Sep 11 2001 pldaniels@pldaniels.com - Cleaned up source and added in more comments - Released v1.2.3 Mon Sep 10 2001 pldaniels@pldaniels.com - changed the detection string for quoted-printable from "quote-printable" to "quoted-printable". A small, but oh so painful mistake! - changed boundary line detection in header files to improve the detection and extraction of multiply-nested mailpacks Sun Sep 9 2001 pldaniels@pldaniels.com - Improved name= parameter discrimination when headers have mixed in components. Now correctly decodes name= when the headers have a type identifier. - Changed XAM_strncasecmp and strlower to use inline/macro version of isupper / tolower Wed Aug 29 2001 pldaniels@pldaniels.com - Added ability for ripMIME to uncompress BASE64 encoded inline mailpacks (these are like forwarded emails, but they've been coded up in BASE64 format! I kid you not!) - Added Handling for Quoted-Printable filenames Fri Aug 24 2001 pldaniels@pldaniels.com - Added recursive calling if mailpack contained encoded mailpack ie, when we get a named rfc822/message type attachment. - Inserting new code from MIME_headers.[ch] which will do explicit analysis of the headers in a single-line stream format. Makes things far far easier to deal with later. Tue Aug 21 2001 pldaniels@pldaniels.com - mime.c: added explicit handling of mailbox format (/var/spool/mail) Fri Aug 03 2001 pldaniels@pldaniels.com - mime.c: added --no_paranoid option for filename filtering Wed Aug 01 2001 pldaniels@pldaniels.com - mime.c: MIME_test_uniquename(): used strrchr rather than XAM_strtok() to locate the last '.' seperator for filenames. Fixes problem with misnaming of multiple .'d filenames. Tue Jul 31 2001 pldaniels@pldaniels.com - XAM_strtok.c: Corrected end-of-tokens bug in XAM_strtok(), would return a null string when it should return the remainder of the string. - mime.c: added additional method for renaming of file --prefix --infix --postfix - mime.c: added "--unique_name" function to preserve existing filenames when decoding (ie, no clobbering) Tue Jun 12 2001 pldaniels@pldaniels.com - mime.c: replaced strncasecmp() with XAM_strncasecmp() to permit portability to sysV and others - mime.c: replaced random() call with rand() - Added early exit routine for base64 if it detects two consecutive hyphens ('-'), this is needed in order to deal with 1-in-1000 situation where a small file is encoded, and there is no separating double CR\LF between the end of the base64 encoding, and the new boundary line. Sun Jun 03 2001 pldaniels@pldaniels.com -Release as 1.0.2 - Fixed occasional issue of a second attachment decoding to 57 bytes in a BASE64 decode, added extra test routine for the length comparisons so that a length of 0 could not be accepted. MIME_decode_64: else if (p_length == -1) { ++ if (t_length > 0) p_length = t_length; } - Replaced strtok() calls with own XAM_strtok() from the XaMime project NOTE- This strtok call DOES NOT compress consecutive delimeters like strtok. The behaviour of XAM_strtok() is more like that of strsep(). The replacement was required to ensure thread-safety, as strtok() is far from being thread-safe (it's bad enough when you 'accidently' nest strtok() calls!. XAM_strtok() can be safely nested. Mon May 21 2001 pldaniels@pldaniels.com - Fixed issue of ripMIME not successfully extracting a RFC822 "embedded" pack if it started with "From" or ">From" Mon May 14 2001 pldaniels@pldaniels.com - Replaced fgetc() calls in BASE64 decoding routine with a set of routines as defined in the new module ffget.[ch]. These new routines read in the MIME encoded file line at a time using fgets() and then feed out the file char at a time using the call FFGET_fgetc(). Speed improvement is nearly DOUBLE thanks to this routine (the more BASE64 decodes you do the better. Mon Apr 02 20:54:45 EST 2001 pldaniels - Added new var, _no_nameless[= 0]. If set to something other than zero, then any attachments without an internally specified filename will be removed (After processing) - Added CLI param to turn on no_nameless. Wed Mar 28 14:26:06 EST 2001 pldaniels - Added patch for filename cleaning. Fixed patch to not overwrite '.'s - Put MIME_clean_MIME_filename() into action, hopefully ISO filenames should be supported without kludgy characters now. Tue Mar 27 21:39:24 EST 2001 pldaniels - Added MIME_get_attachment_count() which returns how many filename'd attachments were found. - Added in _current_line for the purpose of debugging, good for being able to tell where ripMIME is through the reading of a mailpack - Added "default" encoding method as being PLAINTEXT in the event that there is no encoding format handled specifically (Thanks to Hans Arder for supplying mailpack to display this) Tue Mar 21 -PR5 -a debugging line in the collect_headers fn was causing potential encoding-format loss due to absorbing the vital Content-Type line. Fri Mar 16 -Prerelease 4 -Implemented a stack based boundary layer detection system to allow for nested in-line attachments, as typically generated by drag'n'dropping files to email -ISO filename decoding commenced, needing more examples of mailpacks with ISO encoded filenames! -Created the "daily update" version of ripMIME, to follow suit with the new Inflex style of releases. Tue Feb 13 -Prerelease 3 -Fixed up some quoted-printable glitches (pack supplied by James Cownie, thanks!) Tue Jan 30 00:49:51 EST 2001 worker -Prerelease 2 -Removed tmpnam() call out of MIME_XHeader() function, not that this is actually ever used by ripMIME (its part of the cInflex suite) thanks to both Dave DeMaagd and Justin (SuidAfrika) for pointing this out to me. - Corrected directory naming spec so that there aren't double //'s (Thanks to James Cownie for preliminary notification) -Changed the 'From' line detection routine to work with both un-read (ones with just 'From joe@xxx.bbbb') as well as READ emails (ones with '>From joe@xxx.bbbb'). Thanks to Sven for pointing this out (Im too used to using pop3-pickup ;) Also allowed the FROM line to be detected in the MIME_find_next_header() function. Sun Jan 28 15:10:49 EST 2001 worker -v1.0.0 PreRelease 1 -Added in multiple-email (sequential) and nested (forwarded) email attachment extraction support (painful!) -Passed ripMIME through several thousand "real life" emails to verify extraction ability -Modified flag passing structures to contain most in struct _MIME_info (required to get nested emails to extract correctly) -Allowed user to turn on/off each logging option (syslog or stderr) -Tried cleaning up comments a bit -Added in more _debug() calls, making watching the process of decoding via syslog() a bit easier. Fri Jan 12 11:00:12 EST 2001 worker - Added patch supplied by Rolf to also counter for boundary specifiers which dont' have enclosing "'s Tue Jan 09 13:38:16 EST 2001 worker (0.1.10) -Added ability to handle mailpacks which had no CR/LF separator after the end of BASE64 decoding (Thanks to Hans Harder for suppling the mailpack!) Sun Jan 07 00:02:19 EST 2001 worker (0.1.9) - Removed error reporting code from mime.c which said that end-of-file had been reached, this is no longer required as we correctly handle plain-text (into the _headers_ file) -Considering to release as v1.0.0 Sat Dec 30 23:47:18 SAST 2000 worker (0.1.8) - Added print-quotable decoder - release 0.1.9 Thu Dec 28 22:13:56 SAST 2000 worker - Handler for mailpacks WITHOUT a boundary specified (single file emails) - Forgot to change the version indicator in 0.1.7 Sun Dec 24 23:32:09 SAST 2000 worker - Modify base64 decoder to allow a _MAXIMUM_ of 76 chars on the line. and capable of any length of line less than 76 chars. - Release 0.1.7 Fri Dec 22 23:02:57 SAST 2000 worker Commenced fixing ripMIME for new style of MIME headers which have multiple statements on a single line. Sat Nov 25 17:45:45 SAST 2000 worker version 0.1.6 -Applied patch from Matthew to allow input from STDIN -Added feature to allow headers to be dumped to a file (for picking up kak virus etc) -Added more comments to source code Thu Nov 23 18:30:20 SAST 2000 worker version 0.1.5 -Corrected EOL detection for 76 char MIME encodings Thu Nov 23 18:30:20 SAST 2000 worker version 0.1.4 -Corrected End of encoding MIME breakage -Added better -DDEBUG reporting via syslog() Mon Oct 30 09:45:04 SAST 2000 worker version 0.1.3 -Added input parameters to allow for more control -Added ability to specify own text-only filename prefix Mon Oct 30 00:46:05 SAST 2000 worker version 0.1.2 -Added code to remove zero byte files. Sun Oct 29 18:16:46 SAST 2000 worker version 0.1.1 released. p3scan-2.3.2/ripmime-1.4.0.6/build_ripOLE0000755000175000001440000000004610041075743016247 0ustar jlaiusers#!/bin/sh cd ripOLE make clean make p3scan-2.3.2/ripmime-1.4.0.6/build_tnef0000755000175000001440000000003010347164530016044 0ustar jlaiusers#!/bin/sh cd tnef make p3scan-2.3.2/ripmime-1.4.0.6/mime.c0000644000175000001440000033145710347164530015122 0ustar jlaiusers/*----------------------------------------------------------------------- ** ** ** mime.c ** ** Written by Paul L Daniels, originally for the Xamime project ** (http://www.xamime.com) but since spawned off to the ripMIME/alterMIME ** family of email parsing tools. ** ** Copyright PLD, 1999,2000,2001,2002,2003 ** Licence: BSD ** For more information on the licence and copyrights of this code, please ** email copyright@pldaniels.com ** CHANGES ** 2003-Jun-24: PLD: Added subject retaining in the global struct ** this is useful for when you want to retrieve such information ** from an external application - without having to dive into ** the hinfo struct directly. Also, the subject is retained only ** for the /primary/ headers, all subsequent headers which [may] ** contain 'subject:' are ignored. ** */ #include #include #include #include #include #include #include #include #include #include #ifdef MEMORY_DEBUG #define DEBUG_MEMORY 1 #include "xmalloc.h" #endif #include "pldstr.h" #include "boundary-stack.h" #include "ffget.h" #include "mime.h" #include "tnef/tnef_api.h" #include "ripOLE/ole.h" #include "libmime-decoders.h" #include "uuencode.h" #include "filename-filters.h" #include "logger.h" #include "strstack.h" #include "MIME_headers.h" int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ); int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ); int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ); // Predefined filenames #define MIME_BLANKZONE_FILENAME_DEFAULT "_blankzone_" #define MIME_HEADERS_FILENAME "_headers_" #ifndef FL #define FL __FILE__, __LINE__ #endif #define _ENC_UNKNOWN 0 #define _ENC_BASE64 1 #define _ENC_PLAINTEXT 2 #define _ENC_QUOTED 3 #define _ENC_EMBEDDED 4 #define _ENC_NOFILE -1 #define _MIME_CHARS_PER_LINE 32 #define _MIME_MAX_CHARS_PER_LINE 76 #define _RECURSION_LEVEL_DEFAULT 20 #define _BOUNDARY_CRASH 2 // BASE64 / UUDEC and other binary writing routines use the write buffer (now in v1.2.16.3+) // The "limit" define is a check point marker which indicates that on our next run through // either the BASE64 or UUDEC routines, we need to flush the buffer to disk #define _MIME_WRITE_BUFFER_SIZE 102400 #define _MIME_WRITE_BUFFER_LIMIT (_MIME_WRITE_BUFFER_SIZE -4) // Debug precodes #define MIME_DPEDANTIC ((glb.debug >= _MIME_DEBUG_PEDANTIC)) #define MIME_DNORMAL ((glb.debug >= _MIME_DEBUG_NORMAL )) #define MIME_VERBOSE ((glb.verbosity > 0 )) #define MIME_VERBOSE_12 ((glb.verbosity_12x_style > 0 )) #define DMIME if (glb.debug >= _MIME_DEBUG_NORMAL) #define FL __FILE__,__LINE__ /* our base 64 decoder table */ static unsigned char b64[256]={ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \ }; struct MIME_globals { int header_defect_count; int filecount; char blankfileprefix[_MIME_STRLEN_MAX]; int blankfileprefix_expliticly_set; int verbosity; int verbosity_12x_style; int verbosity_contenttype; int verbose_defects; int report_mime; int debug; int quiet; int syslogging; int stderrlogging; int unique_names; int rename_method; char headersname[_MIME_STRLEN_MAX]; char tempdirectory[_MIME_STRLEN_MAX]; int save_headers; int attachment_count; int current_line; int no_nameless; int name_by_type; int mailbox_format; int decode_uu; int decode_tnef; int decode_b64; int decode_qp; int decode_mht; int decode_ole; int multiple_filenames; int header_longsearch; int max_recursion_level; int blankzone_saved; int blankzone_save_option; char blankzone_filename[_MIMEH_STRLEN_MAX +1]; int (*filename_decoded_reporter)(char *, char *); // Pointer to the reporting function for filenames as they are decoded // First subject is an important item, because this // represents the "main" subject of the email, as // seen by the MUA. We have to store this locally // rather than rely purely on hinfo, because other // wise, any consequent parsing of sub-message bodies // will result in the clobbering of the hinfo struct char subject[_MIME_STRLEN_MAX]; }; static struct MIME_globals glb; //char OK[]="OKAY"; static char scratch[1024]; /* File pointer for the headers output */ FILE *headers; /*-----------------------------------------------------------------\ Function Name : MIME_version Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_version( void ) { fprintf(stderr,"ripMIME: %s\n", LIBMIME_VERSION); MIMEH_version(); #ifdef RIPOLE OLE_version(); #endif return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_name_by_type Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Used to set (on/off) the name-by-type feature, which will use the content-type paramter to name the attachment rather than just the default textfile* naming 0 = don't name by type 1 = name by type. We may consider adding more levels to this to account for the different types of content-types. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_name_by_type( int level ) { glb.name_by_type = level; return glb.name_by_type; } /*------------------------------------------------------------------------ Procedure: MIME_set_debug ID:1 Purpose: Sets the debug level for reporting in MIME Input: int level : What level of debugging to use, currently there are only two levels, 0 = none, > 0 = debug info Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_debug( int level ) { glb.debug = level; BS_set_debug(level); TNEF_set_debug(level); MIMEH_set_debug(level); MDECODE_set_debug(level); UUENCODE_set_debug(level); FNFILTER_set_debug(level); return glb.debug; } /*-----------------------------------------------------------------\ Function Name : MIME_set_quiet Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_quiet( int level ) { glb.quiet = level; MIME_set_debug(0); MIME_set_verbosity(0); return glb.quiet; } /*-----------------------------------------------------------------\ Function Name : MIME_set_recursion_level Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_recursion_level( int level ) { glb.max_recursion_level = level; return glb.max_recursion_level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_tnef Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_tnef( int level ) { glb.decode_tnef = level; TNEF_set_decode( level ); return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_ole Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_ole( int level ) { glb.decode_ole = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_uudecode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_uudecode( int level ) { glb.decode_uu = level; UUENCODE_set_decode( level ); return glb.decode_uu; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_base64 Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_base64( int level ) { glb.decode_b64 = level; return glb.decode_b64; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_qp Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_qp( int level ) { glb.decode_qp = level; MDECODE_set_decode_qp(level); return glb.decode_qp; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_doubleCR Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_doubleCR( int level ) { MIMEH_set_doubleCR_save(level); /** 20041106-0859:PLD: Was '0' **/ return MIMEH_get_doubleCR_save(); } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_mht Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_mht( int level ) { glb.decode_mht = level; return glb.decode_mht; } /*-----------------------------------------------------------------\ Function Name : MIME_set_header_longsearch Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_header_longsearch( int level ) { glb.header_longsearch = level; return glb.header_longsearch; } /*------------------------------------------------------------------------ Procedure: MIME_set_tmpdir ID:1 Purpose: Sets the internal Temporary directory name. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_tmpdir( char *setto ) { PLD_strncpy(glb.tempdirectory,setto, _MIME_STRLEN_MAX); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_glb.blankfileprefix ID:1 Purpose: Sets the filename prefix which is to be used for any files which are saved and do not have a defined filename. Input: char *prefix : \0 terminated character array defining the filename prefix Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_blankfileprefix( char *prefix ) { PLD_strncpy( glb.blankfileprefix, prefix, _MIME_STRLEN_MAX ); glb.blankfileprefix_expliticly_set = 1; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_get_glb.blankfileprefix ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ char *MIME_get_blankfileprefix( void ) { return glb.blankfileprefix; } /*------------------------------------------------------- * MIME_setglb.verbosity * * By default, MIME reports nothing as its working * Setting the verbosity level > 0 means that it'll * report things like the name of the files it's * writing/extracting. * */ int MIME_set_verbosity( int level ) { glb.verbosity = level; MIMEH_set_verbosity( level ); TNEF_set_verbosity( level ); FNFILTER_set_verbose( level ); UUENCODE_set_verbosity( level ); MDECODE_set_verbose( level ); BS_set_verbose( level ); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbosity_12x_style Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbosity_12x_style( int level ) { glb.verbosity_12x_style = level; MIME_set_verbosity( level ); return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbosity_contenttype( int level ) { glb.verbosity_contenttype = level; MIMEH_set_verbosity_contenttype( level ); UUENCODE_set_verbosity_contenttype( level ); TNEF_set_verbosity_contenttype( level ); return glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbose_defects Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbose_defects( int level ) { glb.verbose_defects = level; return glb.verbose_defects; } /*-----------------------------------------------------------------\ Function Name : MIME_set_report_MIME Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_report_MIME( int level ) { glb.report_mime = level; /**MIMEH_set_report_MIME(glb.report_mime); - not yet implemented **/ return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { glb.filename_decoded_reporter = ptr_to_fn; UUENCODE_set_filename_report_fn( ptr_to_fn ); TNEF_set_filename_report_fn( ptr_to_fn ); return 0; } /*------------------------------------------------------- * MIME_set_dumpheaders * * By default MIME wont dump the headers to a text file * but at times this is useful esp for checking * for new styles of viruses like the KAK.worm * * Anything > 0 will make the headers be saved * */ int MIME_set_dumpheaders( int level ) { glb.save_headers = level; return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_multiple_filenames Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_multiple_filenames( int level ) { glb.multiple_filenames = level; return 0; } /*------------------------------------------------------ * MIME_set_headersname * * by default, the headers would be dropped to a file * called '_headers_'. Using this call we can make * it pretty much anything we like */ int MIME_set_headersname( char *fname ) { PLD_strncpy(glb.headersname, fname, _MIME_STRLEN_MAX); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_get_headersname ID:1 Purpose: Returns a pointer to the current glb.headersname string. Input: Output: Errors: ------------------------------------------------------------------------*/ char *MIME_get_headersname( void ) { return glb.headersname; } /*-----------------------------------------------------------------\ Function Name : *MIME_get_subject Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIME_get_subject( void ) { return glb.subject; } #ifdef RIPMIME_BLANKZONE /* The blankzone functions are responsbile for telling ripMIME what to do about the block of data which resides between the first/main headers and the first attachment/block of a MIME encoded email. Normally this would come out as textfile0, -except- for non-MIME encoded emails which would actually have their real body saved as textfile0. There are potential kludge options for doing this, but I am going to try and do it right. */ int MIME_set_blankzone_save_option( int option ) { switch ( option ) { case MIME_BLANKZONE_SAVE_TEXTFILE: glb.blankzone_save_option = option; break; case MIME_BLANKZONE_SAVE_FILENAME: glb.blankzone_save_option = option; snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT ); break; default: LOGGER_log("%s:%d:MIME_set_blankzone_save_option:WARNING: Unknown option for saving method (%d). Setting to '%s'",FL, option, MIME_BLANKZONE_FILENAME_DEFAULT ); glb.blankzone_save_option = MIME_BLANKZONE_SAVE_FILENAME; snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT ); } return glb.blankzone_save_option; } /*-----------------------------------------------------------------\ Function Name : MIME_set_blankzone_filename Returns Type : int ----Parameter List 1. char *filename , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_blankzone_filename( char *filename ) { PLD_strncpy( glb.blankzone_filename, filename, _MIME_STRLEN_MAX); return 0; } /*-----------------------------------------------------------------\ Function Name : *MIME_get_blankzone_filename Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIME_get_blankzone_filename( void ) { return glb.blankzone_filename; } #endif /*------------------------------------------------------------------------ Procedure: MIME_setglb.no_nameless ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_no_nameless( int level ) { glb.no_nameless = level; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_uniquenames ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_uniquenames( int level ) { glb.unique_names = level; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_noparanoid ID:1 Purpose: If set, will prevent MIME from clobbering what it considers to be non-safe characters in the file name. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_paranoid( int level ) { FNFILTER_set_paranoid( level ); return level; } /*------------------------------------------------------------------------ Procedure: MIME_set_mailboxformat ID:1 Purpose: If sets the value for the _mailboxformat variable in MIME, this indicates to functions later on that they should be aware of possible mailbox format specifiers. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_mailboxformat( int level ) { glb.mailbox_format = level; MIMEH_set_mailbox( level ); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_renamemethod ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_renamemethod( int method ) { if (( method >= _MIME_RENAME_METHOD_INFIX ) && ( method <= _MIME_RENAME_METHOD_POSTFIX )) { glb.rename_method = method; } else { LOGGER_log("%s:%d:MIME_set_renamemethod:ERROR: selected method not within %d > x > %d range", FL, _MIME_RENAME_METHOD_INFIX, _MIME_RENAME_METHOD_POSTFIX ); return -1; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_get_header_defect_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_get_header_defect_count( void ) { return glb.header_defect_count; } /*------------------------------------------------------------------------ Procedure: MIME_getglb.attachment_count ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_get_attachment_count( void ) { return glb.attachment_count; } /*------------------------------------------------------------------------ Procedure: MIME_test_uniquename ID:1 Purpose: Checks to see that the filename specified is unique. If it's not unique, it will modify the filename Input: char *path: Path in which to look for similar filenames char *fname: Current filename int method: Method of altering the filename (infix, postfix, prefix) Output: Errors: ------------------------------------------------------------------------*/ int MIME_test_uniquename( char *path, char *fname, int method ) { struct stat buf; char newname[_MIME_STRLEN_MAX +1]; char scr[_MIME_STRLEN_MAX +1]; /** Scratch var **/ char *frontname, *extention; int cleared = 0; int count = 1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Start (%s)",FL,fname); frontname = extention = NULL; // shuts the compiler up if (method == _MIME_RENAME_METHOD_INFIX) { PLD_strncpy(scr,fname, _MIMEH_STRLEN_MAX); frontname = scr; extention = strrchr(scr,'.'); if (extention) { *extention = '\0'; extention++; } else { method = _MIME_RENAME_METHOD_POSTFIX; } } snprintf(newname,_MIME_STRLEN_MAX,"%s/%s",path,fname); while (!cleared) { if ((stat(newname, &buf) == -1)) { cleared++; } else { if (method == _MIME_RENAME_METHOD_PREFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%d_%s",path,count,fname); } else if (method == _MIME_RENAME_METHOD_INFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d.%s",path,frontname,count,extention); } else if (method == _MIME_RENAME_METHOD_POSTFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d",path,fname,count); } count++; } } if (count > 1) { frontname = strrchr(newname,'/'); if (frontname) frontname++; else frontname = newname; PLD_strncpy(fname, frontname, _MIMEH_FILENAMELEN_MAX); //FIXME - this assumes that the buffer space is at least MIME_STRLEN_MAX sized. } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Done (%s)",FL,fname); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_is_file_mime ID:1 Purpose: Determines if the file handed to it is a MIME type email file. Input: file name to analyze Output: Returns 0 for NO, 1 for YES, -1 for "Things di Errors: ------------------------------------------------------------------------*/ int MIME_is_file_RFC822( char *fname ) { char conditions[16][16] = { "Received: ", "From: ", "Subject: ", "Date: ", "Content-", "content-", "from: ", "subject: ", "date: ", "boundary=", "Boundary=" }; int result = 0; int hitcount = 0; int linecount = 100; // We should only need to read the first 10 lines of any file. char *line; FILE *f; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Testing %s for RFC822 headers",FL,fname); f = fopen(fname,"r"); if (!f) { if (glb.quiet == 0) { LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot open file '%s' for reading (%s)", FL, fname,strerror(errno)); } return 0; } line = malloc(sizeof(char) *1025); if (!line) { LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot allocate memory for read buffer", FL); return 0; } while ((hitcount < 2)&&(fgets(line,1024,f))&&(linecount--)) { /** test every line for possible headers, until we get a blank line **/ if ((glb.header_longsearch == 0)&&(*line == '\n' || *line == '\r')) break; for (result = 0; result < 11; result++) { /** Test for every possible MIME header prefix, ie, From: Subject: etc **/ if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_is_file_mime:DEBUG: Testing for '%s' in '%s'", FL, line, conditions[result]); if (strncasecmp(line,conditions[result],strlen(conditions[result]))==0) { /** If we get a match, then increment the hit counter **/ hitcount++; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit on %s",FL,conditions[result]); } } } fclose(f); if (hitcount >= 2) result = 1; else result = 0; if (line) free(line); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit count = %d, result = %d",FL,hitcount,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_getchar_start ID:1 Purpose: This function is used on a once-off basis. It's purpose is to locate a non-whitespace character which (in the context of its use) indicates that the commencement of BASE64 encoding data has commenced. Input: FFGET f: file stream Output: First character of the BASE64 encoding. Errors: ------------------------------------------------------------------------*/ int MIME_getchar_start( FFGET_FILE *f ) { int c; /* loop for eternity, as we're "returning out" */ while (1) { /* get a single char from file */ c = FFGET_fgetc(f); /* if that char is an EOF, or the char is something beyond * ASCII 32 (space) then return */ if ((c == EOF) || (c > ' ')) { return c; } } /* while */ /* Although we shouldn't ever actually get here, we best put it in anyhow */ return EOF; } /*------------------------------------------------------------------------ Procedure: MIME_decode_TNEF ID:1 Purpose: Decodes TNEF encoded attachments Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_TNEF( char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { int result=0; char fullpath[1024]; snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); TNEF_set_path(unpackdir); result = TNEF_main( fullpath ); if (result >= 0) { // result = remove( fullpath ); if (result == -1) { if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_decode_TNEF: Removing %s failed (%s)",FL,fullpath,strerror(errno)); } } return result; } #ifdef RIPOLE int MIME_report_filename_decoded_RIPOLE(char *filename) { LOGGER_log("Decoding filename=%s", filename); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_decode_OLE Returns Type : int ----Parameter List 1. char *unpackdir, 2. struct MIMEH_header_info *hinfo, 3. int keep , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_decode_OLE( char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { struct OLE_object ole; char fullpath[1024]; int result; snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); OLE_init(&ole); OLE_set_quiet(&ole,glb.quiet); OLE_set_verbose(&ole,glb.verbosity); OLE_set_debug(&ole,glb.debug); OLE_set_save_unknown_streams(&ole,0); OLE_set_filename_report_fn(&ole, MIME_report_filename_decoded_RIPOLE ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Starting OLE Decode",FL); result = OLE_decode_file(&ole, fullpath, unpackdir ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode done, cleaning up.",FL); OLE_decode_file_done(&ole); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode returned with code = %d",FL,result); return result; } #endif /*------------------------------------------------------------------------ Procedure: MIME_decode_raw ID:1 Purpose: Decodes a binary type attachment, ie, no encoding, just raw data. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_raw( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { int result = 0; char fullpath[1024]; int bufsize=1024; char *buffer = malloc((bufsize +1)*sizeof(char)); int readcount; int file_has_uuencode = 0; int decode_entire_file = 0; FILE *fo; /* Decoding / reading a binary attachment is a real interesting situation, as we * still use the fgets() call, but we do so repeatedly until it returns a line with a * \n\r and the boundary specifier in it.... all in all, I wouldn't rate this code * as being perfect, it's activated only if the user intentionally specifies it with * --binary flag */ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Start\n",FL); snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); fo = fopen(fullpath,"wb"); if (!fo) { LOGGER_log("%s:%d:MIME_decode_raw:ERROR: cannot open file %s for writing. (%s)\n\n",FL,fullpath,strerror(errno)); return -1; } while ((readcount=FFGET_raw(f, buffer,bufsize)) > 0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: BUFFER[%p]= '%s'\n",FL,buffer, buffer); if ((!file_has_uuencode)&&(UUENCODE_is_uuencode_header( buffer ))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: UUENCODED is YES (buffer=[%p]\n",FL,buffer); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: File contains UUENCODED data(%s)\n",FL,buffer); file_has_uuencode = 1; } if (BS_cmp(buffer, readcount)) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Boundary located - breaking out.\n",FL); break; } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: writing: %s\n",FL, buffer); fwrite(buffer,sizeof(char),readcount,fo); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Completed reading RAW data\n",FL); free(buffer); fclose(fo); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Closed file and free'd buffer\n",FL); // If there was UUEncoded portions [potentially] in the email, the // try to extract them using the MIME_decode_uu() if (file_has_uuencode) { char full_decode_path[512]; snprintf(full_decode_path,sizeof(full_decode_path),"%s/%s",unpackdir,hinfo->filename); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding UUencoded data\n",FL); if ( hinfo->content_transfer_encoding == _CTRANS_ENCODING_UUENCODE ) decode_entire_file = 0; //result = UUENCODE_decode_uu(NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep ); result = UUENCODE_decode_uu(NULL, unpackdir, full_decode_path, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep ); if (result == -1) { switch (uuencode_error) { case UUENCODE_STATUS_SHORT_FILE: case UUENCODE_STATUS_CANNOT_OPEN_FILE: case UUENCODE_STATUS_CANNOT_FIND_FILENAME: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error); result = 0; break; case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL); return -1; break; default: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error); return -1; } } if (result == UUENCODE_STATUS_SHORT_FILE) result = 0; glb.attachment_count += result; if (strlen(hinfo->uudec_name)) { if (strcasecmp(hinfo->uudec_name,"winmail.dat")==0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding TNEF format\n",FL); snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name); MIME_decode_TNEF( unpackdir, hinfo, keep); } else LOGGER_log("%s:%d:MIME_decode_raw:WARNING: hinfo has been clobbered.\n",FL); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: End[result = %d]\n",FL,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_text ID:1 Purpose: Decodes an input stream into a text file. Input: unpackdir : directory where to place new text file hinfo : struct containing information from the last parsed headers keep : if set, retain the file Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_text( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { FILE *of; // output file int linecount = 0; // The number of lines int file_has_uuencode = 0; // Flag to indicate this text has UUENCODE in it char fullfilename[1024]=""; // Filename of the output file char line[1024]; // The input lines from the file we're decoding char *get_result = &line[0]; int lastlinewasboundary = 0; int result = 0; int decodesize=0; snprintf(fullfilename,sizeof(fullfilename),"%s/%s",unpackdir,hinfo->filename); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TEXT [encoding=%d] to %s\n",FL, hinfo->content_transfer_encoding, fullfilename); if (!f) { /** If we cannot open the file for reading, leave an error and return -1 **/ LOGGER_log("%s:%d:MIME_decode_text:ERROR: print-quotable input stream broken.",FL); return -1; } if (f) { /** If we were able to open the input file, try opening the output file and process the data **/ of = fopen(fullfilename,"w"); if (!of) { /** If we were unable to open the output file, report the error and return -1 **/ LOGGER_log("%s:%d:MIME_decode_text:ERROR: cannot open %s for writing",FL,fullfilename); return -1; } while ((get_result = FFGET_fgets(line,1023,f))&&(of)) { int line_len = strlen(line); linecount++; // if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line=%s",FL,line); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line[len=%d]=%s",FL,line_len,line); //20041217-1529:PLD: if (line[0] == '-') { if ((BS_count() > 0)&&(BS_cmp(line,line_len))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_DNORMAL:DEBUG: Hit a boundary on the line",FL); lastlinewasboundary = 1; result = 0; break; } } if (lastlinewasboundary == 0) { if (hinfo->content_transfer_encoding == _CTRANS_ENCODING_QP) { decodesize = MDECODE_decode_qp_text(line); fwrite(line, 1, decodesize, of); } else { fprintf(of,"%s",line); } if ((!file_has_uuencode)&&( UUENCODE_is_uuencode_header( line ))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: UUENCODED data located in file.\n",FL); file_has_uuencode = 1; } } // linecount++; } // while if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done writing output file '%s'...now attempting to close.",FL, fullfilename); // if the file is still safely open if (of) { fclose(of); } // if file still safely open if (linecount == 0) { result = MIME_STATUS_ZERO_FILE; return result; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Closed.",FL); } // if main input file stream was open // If our input from the file was invalid due to EOF or other // then we return a -1 code to indicate that the end has // occured. // if (!get_result) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: FFGET module ran out of file data while attempting to decode",FL); // result = -1; result = MIME_ERROR_FFGET_EMPTY; // 20040305-1323:PLD } // If there was UUEncoded portions [potentially] in the email, the // try to extract them using the MIME_decode_uu() // if (file_has_uuencode) { char ffname[256]; snprintf(ffname,256,"%s/%s", unpackdir, hinfo->filename); // PLD-20040627-1212 // Make sure uudec_name is blank too // hinfo->uudec_name[0] = '\0'; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding UUencoded data in file '%s'\n",FL,hinfo->filename); //result = UUENCODE_decode_uu( NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep ); // Attempt to decode the UUENCODED data in the file, // NOTE - hinfo->uudec_name is a blank buffer which will be filled by the UUENCODE_decode_uu // function once it has located a filename in the UUENCODED data. A bit of a problem here // is that it can only hold ONE filename! // // NOTE - this function returns the NUMBER of attachments it decoded in the return value! Don't // propergate this value unintentionally to parent functions (ie, if you were thinking it was // an error-status return value result = UUENCODE_decode_uu( NULL, unpackdir, ffname, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep ); if (result == -1) { switch (uuencode_error) { case UUENCODE_STATUS_SHORT_FILE: case UUENCODE_STATUS_CANNOT_OPEN_FILE: case UUENCODE_STATUS_CANNOT_FIND_FILENAME: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error); result = 0; break; case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL); return -1; break; default: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error); return -1; } } if ( result > 0 ) { glb.attachment_count += result; result = 0; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: hinfo = %p\n",FL,hinfo); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done. [ UUName = '%s' ]\n",FL,hinfo->uudec_name); if (strncasecmp(hinfo->uudec_name,"winmail.dat",11)==0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TNEF format\n",FL); snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name); MIME_decode_TNEF( unpackdir, hinfo, keep ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Completed decoding UUencoded data.\n",FL); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: result=%d ----------------Done\n",FL,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_64 ID:1 Purpose: This routine is very very very important, it's the key to ensuring we get our attachments out of the email file without trauma! NOTE - this has been -slightly altered- in order to make provision of the fact that the attachment may end BEFORE the EOF is received as is the case with multiple attachments in email. Hence, we now have to detect the start character of the "boundary" marker I may consider testing the 1st n' chars of the boundary marker just incase it's not always a hypen '-'. Input: FGET_FILE *f: stream we're reading from char *unpackdir: directory we have to write the file to struct MIMEH_header_info *hinfo: Auxillairy information such as the destination filename Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_64( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo ) { int i; int cr_total = 0; int cr_count = 0; /* the number of consecutive \n's we've read in, used to detect End of B64 enc */ int ignore_crcount = 0; /* If we have a boundary in our emails, then ignore the CR counts */ int stopcount = 0; /* How many stop (=) characters we've read in */ int eom_reached = 0; /* flag to say that we've reached the End-Of-MIME encoding. */ int status = 0; /* Overall status of decoding operation */ int c; /* a single char as retrieved using MIME_get_char() */ int char_count = 0; /* How many chars have been received */ int boundary_crash = 0; /* if we crash into a boundary, set this */ long int bytecount=0; /* The total file decoded size */ char output[3]; /* The 4->3 byte output array */ char input[4]; /* The 4->3 byte input array */ char fullMIME_filename[_MIME_STRLEN_MAX]=""; /* Full Filename of output file */ // Write Buffer routine unsigned char *writebuffer; unsigned char *wbpos; int wbcount = 0; int loop; FILE *of; /* output file pointer */ /* generate the MIME_filename, and open it up... */ if (glb.unique_names) MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method ); snprintf(fullMIME_filename,_MIME_STRLEN_MAX,"%s/%s",unpackdir,hinfo->filename); of = fopen(fullMIME_filename,"wb"); /* if we were unable to open the output file, then we better log an error and drop out */ if (!of) { LOGGER_log("%s:%d:MIME_decode_64:ERROR: Cannot open output file %s for BASE64 decoding.",FL,fullMIME_filename); // exit(_EXITERR_BASE64_OUTPUT_NOT_OPEN); return -1; } // Allocate the write buffer. By using the write buffer we gain an additional 10% in performance // due to the lack of function call (fwrite) overheads writebuffer = malloc( _MIME_WRITE_BUFFER_SIZE *sizeof(unsigned char)); if (!writebuffer) { LOGGER_log("%s:%d:MIME_decode_64:ERROR: cannot allocate 100K of memory for the write buffer",FL); return -1; } else { wbpos = writebuffer; wbcount = 0; } /* Set our ignore_crcount flag */ if (BS_count() > 0) { //LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Ignore CR set to 1",FL); ignore_crcount = 1; } /* collect prefixing trash (if any, such as spaces, CR's etc)*/ // c = MIME_getchar_start(f); /* and push the good char back */ // FFGET_ungetc(f,c); c = '\0'; /* do an endless loop, as we're -breaking- out later */ while (1) { int lastchar_was_linebreak=0; /* Initialise the decode buffer */ input[0] = input[1] = input[2] = input[3] = 0; // was '0' - Stepan Kasal patch /* snatch 4 characters from the input */ for (i = 0; i < 4; i++) { // Get Next char from the file input // // A lot of CPU is wasted here due to function call overheads, unfortunately // I cannot yet work out how to make this call (FFGET) operate effectively // without including a couple of dozen lines in this section. // // Times like this C++'s "inline" statement would be nice. // //----------INLINE Version of FFGET_getchar() // do { if ((c == '\n')||(c == '\r')) lastchar_was_linebreak = 1; else lastchar_was_linebreak = 0; if (f->ungetcset) { f->ungetcset = 0; c = f->c; } else { if ((!f->startpoint)||(f->startpoint > f->endpoint)) { FFGET_getnewblock(f); } if (f->startpoint <= f->endpoint) { c = *f->startpoint; f->startpoint++; } else { c = EOF; } } } while ( (c != EOF) && ( c < ' ' ) && ( c != '\n' ) && (c != '-') && (c != '\r') ); // //-------END OF INLINE--------------------------------------------- if ((ignore_crcount == 1)&&(c == '-')) //&&(lastchar_was_linebreak == 1)) { int hit = 0; char *p; p = strchr((f->startpoint -1), '\n'); if (p != NULL) *p = '\0'; hit = BS_cmp((f->startpoint -1),strlen(f->startpoint) +1); // LOGGER_log("%s:%d:MIME_decode_64:DEBUG: leader '-' found at %s",FL,(f->startpoint -1)); if (p != NULL) *p = '\n'; if (hit > 0) { // LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Boundary detected and breaking out ",FL); // FFGET_fgets(scratch,sizeof(scratch),f); // eom_reached = 1; boundary_crash = 1; break; } else { /** 20041105-22H56:PLD: Applied Stepan Kasal patch **/ i--; continue; } } // If we get a CR then we need to check a few things, namely to see // if the BASE64 stream has terminated ( indicated by two consecutive // \n's // // This should not be affected by \r's because the \r's will just pass // through and be absorbed by the detection of possible 'invalid' // characters if ((ignore_crcount == 0)&&(c == '\n')) { cr_count++; cr_total++; // Because there are some mail-agents out there which are REALLY naughty // and do things like sticking random blank lines into B64 data streams // (why o why!?) we need to have a settable option here to decide if // we want to test for double-CR breaking or just ignore it // // To avoid this, we relax the double-CR extention, and say "Okay, we'll let // you have a single blank line - but any more than that and we'll slap you // hand". Honestly, I do not like this solution, but it seems to be the only // way to preserve decoding accurancy without resorting to having to use // a command line switch to dictate which method to use ( NO-CR's or ignore // CR's ). if (cr_count > 2) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: EOF Reached due to two consecutive CR's on line %d\n",FL,cr_total); eom_reached++; break; } else { char_count = 0; i--; continue; } // else if it wasn't our 3rd CR } else { cr_count=0; } /* if we get an EOF char, then we know something went wrong */ if ( c == EOF ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: input stream broken for base64 decoding for file %s. %ld bytes of data in buffer to be written out\n",FL,hinfo->filename,wbcount); status = MIME_ERROR_B64_INPUT_STREAM_EOF; fwrite(writebuffer, 1, wbcount, of); fclose(of); if (writebuffer) free(writebuffer); return status; break; } /* if c was the EOF */ else if (c == '=') { // Once we've found a stop char, we can actually just "pad" in the rest // of the stop chars because we know we're at the end. Some MTA's dont // put in enough stopchars... at least it seems X-MIMEOLE: Produced By Microsoft MimeOLE V5.50.4133.2400 // doesnt. if (i == 2) { input[2] = input[3] = (char)b64[c]; } else if (i == 3) { input[3] = (char)b64[c]; } // NOTE------ // Previously we relied on the fact that if we detected a stop char, that FFGET() // would automatically absorb the data till EOL. This is no longer the case as we // are now only retrieving data byte at a time. // So, now we -absorb- till the end of the line using FFGET_fgets() stopcount = 4 -i; FFGET_fgets(scratch,sizeof(scratch),f); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Stop char detected pos=%d...StopCount = %d\n",FL,i,stopcount); i = 4; break; // out of FOR. } /* else if ((char_count == 0)&&(c == '-' )) { if (FFGET_fgetc(f) == '-') { boundary_crash++; eom_reached++; break; } } */ /* test for and discard invalid chars */ if (b64[c] == 0x80) { i--; continue; } // Finally, if we get this far without the character having been picked // out as some special meaning character, we decode it and place the // resultant byte into the input[] array. input[i] = (char)b64[c]; /* assuming we've gotten this far, then we increment the char_count */ char_count++; } // FOR // now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data output[0] = (input[0] << 2) | (input[1] >> 4); output[1] = (input[1] << 4) | (input[2] >> 2); output[2] = (input[2] << 6) | input[3]; // determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it if (i == 4) { // If our buffer is full beyond the 'write out limit', then we write the buffered // data to the file - We use this method in order to save calling fwrite() too // many times, thus avoiding function call overheads and [ possibly ] disk write // interrupt costs. if ( wbcount > _MIME_WRITE_BUFFER_LIMIT ) { fwrite(writebuffer, 1, wbcount, of); wbpos = writebuffer; wbcount = 0; } // Copy our converted bytes to the write buffer for (loop = 0; loop < (3 -stopcount); loop++) { *wbpos = output[loop]; wbpos++; wbcount++; } // tally up our total byte conversion count bytecount+=(3 -stopcount); } else if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: could not attain 4 bytes input\n",FL); // if we wrote less than 3 chars, it means we were at the end of the encoded file thus we exit if ((eom_reached)||(stopcount > 0)||(boundary_crash)||(i!=4)) { // Write out the remaining contents of our write buffer - If we don't do this // we'll end up with truncated files. if (wbcount > 0) { fwrite(writebuffer, 1, wbcount, of); } /* close the output file, we're done writing to it */ fclose(of); /* if we didn't really write anything, then trash the file */ if (bytecount == 0) { // unlink(fullMIME_filename); status = MIME_BASE64_STATUS_ZERO_FILE; } if (boundary_crash) { // Absorb to end of line // status = MIME_BASE64_STATUS_HIT_BOUNDARY; // was _BOUNDARY_CRASH } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: File size = %ld bytes, Exit Status = %d, Boundary Crash = %d\n",FL, bytecount, status, boundary_crash); if (writebuffer) free(writebuffer); return status; } // if End-of-MIME or Stopchars appeared } // while if (writebuffer) free(writebuffer); return status; } /*-----------------------------------------------------------------\ Function Name : MIME_decode_64_cleanup Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *hinfo, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_decode_64_cleanup( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo) { int result = 0; char buffer[128]; while (FFGET_fgets(buffer, sizeof(buffer), f)) { if (FFGET_feof(f) != 0) break; if (BS_cmp(buffer,strlen(buffer)) > 0) break; } return result; } /*------------------------------------------------------------------------ Procedure: MIME_doubleCR_decode ID:1 Purpose: Decodes a text sequence as detected in the processing of the MIME headers. This is a specialised call, not really a normal part of MIME decoding, but is required in order to deal with decyphering MS Outlook visable defects. Input: char *filename: Name of the encoded file we need to decode char *unpackdir: Directory we need to unpack the file to struct MIMEH_header_info *hinfo: Header information already gleaned from the headers int current_recursion_level: How many nest levels we are deep Output: Errors: ------------------------------------------------------------------------*/ int MIME_doubleCR_decode( char *filename, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level ) { int result = 0; struct MIMEH_header_info h; char *p; // PLD:260303-1317 // if ((p=strrchr(filename,'/'))) p++; // else p = filename; p = filename; // * Initialise the header fields h.uudec_name[0] = '\0'; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: filename=%s, path=%s, recursion=%d", FL, filename, unpackdir, current_recursion_level ); memcpy(&h, hinfo, sizeof(h)); // Works for ripMIME snprintf(h.filename, sizeof(h.filename), "%s/%s", unpackdir, p); snprintf(h.filename, sizeof(h.filename), "%s", p); /// Works for Xamime if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: header.filename = %s", FL, h.filename ); if (MIME_is_file_RFC822(filename)) { if (MIME_VERBOSE) LOGGER_log("Attempting to decode Double-CR delimeted MIME attachment '%s'\n",filename); result = MIME_unpack( unpackdir, filename, current_recursion_level ); // 20040305-1303:PLD - Capture the result of the unpack and propagate up } else if (UUENCODE_is_file_uuencoded(h.filename)) { if (MIME_VERBOSE) LOGGER_log("Attempting to decode UUENCODED attachment from Double-CR delimeted attachment '%s'\n",filename); UUENCODE_set_doubleCR_mode(1); result = UUENCODE_decode_uu(NULL, unpackdir, filename, h.uudec_name, _MIMEH_FILENAMELEN_MAX , 1, 1 ); UUENCODE_set_doubleCR_mode(0); glb.attachment_count += result; result = 0; } return result; } /*------------------------------------------------------------------------ Procedure: MIME_read ID:1 Purpose: Reads data from STDIN and saves the mailpack to the filename specified Input: char *mpname: full pathname of the file to save the data from STDIN to Output: Errors: 28-Feb-2003 This function has been modified to use feof() and fread/fwrite calls in order to ensure that binary data in the input does not cause the reading to prematurely terminate [ as it used to prior to the modification ] ------------------------------------------------------------------------*/ int MIME_read( char *mpname ) { long int fsize=-1; char *buffer; int readcount, writecount; FILE *fout; buffer = malloc( 4096 *sizeof(char) ); if ( !buffer ) { LOGGER_log("%s:%d:MIME_read:ERROR: could not allocate 4K of memory for file read buffer\n",FL ); return -1; } /* open up our input file */ fout = fopen(mpname,"w"); /* check that out file opened up okay */ if (!fout) { LOGGER_log("%s:%d:MIME_read:ERROR: Cannot open file %s for writing... check permissions perhaps?",FL,mpname); //exit(_EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT); return -1; } /* assuming our file actually opened up */ if (fout) { fsize=0; /* while there is more data, consume it */ while ( !feof(stdin) ) { readcount = fread( buffer, 1, (4096 *sizeof(char)), stdin ); if ( readcount > 0 ) { writecount = fwrite( buffer, 1, readcount, fout ); fsize += writecount; if ( readcount != writecount ) { LOGGER_log("%s:%d:MIME_read:ERROR: Attempted to write %d bytes, but only managed %d to file '%s'",FL, readcount, writecount, mpname ); } } else { break; } } /* clean up our buffers and close */ fflush(fout); fclose(fout); if ( feof(stdin) ) clearerr(stdin); } /* end if fout was received okay */ if ( buffer ) free( buffer ); /* return our byte count in KB */ return (int)(fsize /1024); } /*------------------------------------------------------------------------ Procedure: MIME_init ID:1 Purpose: Initialise various required parameters to ensure a clean starting of MIME decoding. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_init( void ) { BS_init(); // Boundary-stack initialisations MIMEH_init(); // Initialise MIME header routines. UUENCODE_init(); // uuen:coding decoding initialisations FNFILTER_init(); // Filename filtering MDECODE_init(); // ISO filename decoding initialisation TNEF_init(); // TNEF decoder glb.header_defect_count = 0; glb.filecount = 0; glb.attachment_count = 0; glb.current_line = 0; glb.verbosity = 0; glb.verbose_defects = 0; glb.debug = 0; glb.quiet = 0; glb.syslogging = 0; glb.stderrlogging = 1; glb.unique_names = 0; glb.save_headers = 0; glb.no_nameless = 0; glb.mailbox_format = 0; glb.name_by_type = 0; glb.rename_method = _MIME_RENAME_METHOD_INFIX; glb.header_longsearch = 0; glb.max_recursion_level = _RECURSION_LEVEL_DEFAULT; glb.decode_qp = 1; glb.decode_b64 = 1; glb.decode_tnef = 1; glb.decode_ole = 1; glb.decode_uu = 1; glb.decode_mht = 1; glb.multiple_filenames = 1; glb.blankzone_save_option = MIME_BLANKZONE_SAVE_TEXTFILE; glb.blankzone_saved = 0; snprintf( glb.headersname, sizeof(glb.headersname), "_headers_" ); snprintf( glb.blankfileprefix, sizeof( glb.blankfileprefix ), "textfile" ); glb.blankfileprefix_expliticly_set = 0; glb.subject[0]='\0'; return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_generate_multiple_hardlink_filenames Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: If there is more than one filename/name for a given attachment due to an exploit attempt,then we need to generate the required hardlinks to replicate this in our output. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_generate_multiple_hardlink_filenames(struct MIMEH_header_info *hinfo, char *unpackdir) { char *name; char oldname[1024]; if (glb.multiple_filenames == 0) return 0; //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Generating hardlinks for %s",FL, hinfo->filename); snprintf(oldname,sizeof(oldname),"%s/%s",unpackdir, hinfo->filename); if (SS_count(&(hinfo->ss_names)) > 1){ do { name = SS_pop(&(hinfo->ss_names)); if (name != NULL) { char *np; char newname[1024]; int rv; /** Strip off any leading path **/ np = strrchr(name, '/'); if (np) np++; else np = name; snprintf(newname,sizeof(newname),"%s/%s",unpackdir, np); //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname); rv = link(oldname, newname); if (rv == -1) { if (errno != EEXIST) { LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno)); } } else { if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE)) { glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } } } while(name != NULL); } if (SS_count(&(hinfo->ss_filenames)) > 1) { do { name = SS_pop(&(hinfo->ss_filenames)); if (name != NULL) { char newname[1024]; int rv; snprintf(newname,sizeof(newname),"%s/%s",unpackdir, name); //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname); rv = link(oldname, newname); if (rv == -1) { if (errno != EEXIST) { LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno)); } } else { if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE)) { glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } } } while(name != NULL); } return 0; } /*------------------------------------------------------------------------ Procedure: MIME_decode_encoding ID:1 Purpose: Based on the contents of hinfo, this function will call the required function needed to decode the contents of the file which is contained within the MIME structure Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_encoding( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, struct SS_object *ss ) { int keep = 1; int result = -1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Start:DEBUG: (%s)\n",FL, hinfo->filename); // If we have a valid filename, then put it through the process of // cleaning and filtering if (isprint((int)hinfo->filename[0])) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is valid, cleaning\n",FL); FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX); /* check out thefilename for ISO filenames */ } // If the filename is NOT valid [doesn't have a printable first char] // then we must create a new file name for it. // if (hinfo->filename[0] == '\0') { if (glb.name_by_type == 1) { // If we're to name our nameless files based on the content-type // then we need to get the content-type string from the hinfo // and then strip it of any nasty characters (such as / and \) char *filename_prefix; filename_prefix = strdup( hinfo->content_type_string ); if (filename_prefix != NULL) { char *pp; pp = filename_prefix; while (*pp) { if ((*pp == '/') || (*pp == '\\')) *pp = '-'; pp++; } snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%s%d", glb.blankfileprefix_expliticly_set?glb.blankfileprefix:"", filename_prefix, glb.filecount ); free(filename_prefix); } else { snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount ); } } else { // If we don't care to rename our files based on the content-type // then we'll simply use the blankfileprefix. snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is empty, setting to default...(%s)\n",FL, hinfo->filename); if (glb.no_nameless) keep = 0; glb.filecount++; } else if (strncmp(hinfo->filename, glb.blankfileprefix, strlen(glb.blankfileprefix)) != 0) { // If the filename does not contain the blankfile prefix at the beginning, then we // will consider it a normal attachment, thus, we // need to increment the attachment count glb.attachment_count++; } // If we are required to have "unique" filenames for everything, rather than // allowing ripMIME to overwrite stuff, then we put the filename through // its tests here if ((glb.unique_names)&&(keep)) { MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method ); } // If the calling program requested verbosity, then indicate that we're decoding // the file here if ((keep)&&(MIME_VERBOSE)) { if (MIME_VERBOSE_12) { LOGGER_log("Decoding: %s\n", hinfo->filename); } else { // Now, please bare with me on this horrid little piece of tortured code, // ... basically, now that we're using LOGGER_log to generate our output, we // need to compose everything onto a single LOGGER_log() call, otherwise our // output will get split into several lines, not entirely the most plesant // thing we want to see when we've got another program most likely reading // the output. // // The %s%s pair is DELIBERATELY pressed up against the 'Decoding' word because // otherwise, if we are not outputting any data, we'll end up with a double // space between Decoding and filename, certainly not very good thing to do // if we're trying to present a consistant output. // // The parameters for the content-type output are decided on by the result // of the MIMEH_get_verbosity_contenttype() call. If the call returns > 0 // then the TRUE token is selected to be displayed, else the FALSE token // ( in this case, an empty string "" ) is selected. // The format of the evaluation is: // // (logic-test?true-expression:false-expression) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: About to execute callback [0x%p]",FL,glb.filename_decoded_reporter); if (glb.filename_decoded_reporter != NULL) { glb.filename_decoded_reporter( hinfo->filename, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } // If we were using the new filename telling format } // If we were telling the filename (verbosity) if (1) { char *fp; /** Find the start of the filename. **/ fp = strrchr(hinfo->filename, '/'); if (fp) fp++; else fp = hinfo->filename; //LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Pushing filename %s to the stack",FL,fp); // 20040305-1419:PLD // Store the filename we're going to use to save the file to in the filename stack SS_push(ss, fp, strlen(fp)); } // Select the decoding method based on the content transfer encoding // method which we read from the headers if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: ENCODING = %d\n",FL, hinfo->content_transfer_encoding); switch (hinfo->content_transfer_encoding) { case _CTRANS_ENCODING_B64: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding BASE64 format\n",FL); result = MIME_decode_64(f, unpackdir, hinfo); switch (result) { case MIME_ERROR_B64_INPUT_STREAM_EOF: break; case MIME_BASE64_STATUS_HIT_BOUNDARY: result = 0; break; case 0: result = MIME_decode_64_cleanup(f, unpackdir, hinfo); break; default: break; } break; case _CTRANS_ENCODING_7BIT: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 7BIT format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_8BIT: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 8BIT format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_BINARY: case _CTRANS_ENCODING_RAW: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding RAW format\n",FL); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_QP: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding Quoted-Printable format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_UUENCODE: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UUENCODED format\n",FL); // Added as a test - remove if we can get this to work in a better way snprintf(hinfo->uudec_name,sizeof(hinfo->uudec_name),"%s",hinfo->filename); result = UUENCODE_decode_uu(f, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 0, keep ); glb.attachment_count += result; // Because this is a file-count, it's not really an 'error result' as such, so, set the // return code back to 0! result = 0; break; case _CTRANS_ENCODING_UNKNOWN: switch (hinfo->content_disposition) { case _CDISPOSITION_FORMDATA: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format of FORMDATA disposition\n",FL); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; default: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: UNKNOWN Decode completed, result = %d\n",FL,result); break; case _CTRANS_ENCODING_UNSPECIFIED: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNSPECIFIED format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding result for UNSPECIFIED format = %d\n",FL, result); // 20040114-1236:PLD: Added nested mail checking // // Sometimes mailpacks have false headers at the start, resulting // in ripMIME terminating the recursion process too early. This // little test here checks to see if the output of the file is // a nested MIME email (or even an ordinary email // // It should be noted, that the reason why the test occurs /here/ is // because dud headers will always result in a UNSPECIFIED encoding // // Original sample mailpack was sent by Farit - thanks. if (1) { snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename); LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG:REMOVEME: Testing for RFC822 headers in file %s",FL,hinfo->scratch); if (MIME_is_file_RFC822(hinfo->scratch) > 0 ) { // 20040305-1304:PLD: unpack the file, propagate result upwards result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss ); } } break; default: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding format is not defined (%d)\n",FL, hinfo->content_transfer_encoding); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; } // Analyze our results switch (result) { case 0: break; case MIME_STATUS_ZERO_FILE: return MIME_STATUS_ZERO_FILE; break; case MIME_ERROR_FFGET_EMPTY: return result; break; case MIME_ERROR_RECURSION_LIMIT_REACHED: return result; break; } if ((result != -1)&&(result != MIME_STATUS_ZERO_FILE)) { #ifdef RIPOLE // If we have OLE decoding active and compiled in, then // do a quick attempt at decoding the file, fortunately // because the ripOLE engine detects if the file is not an // OLE file, it does not have a major impact on the // performance of the ripMIME decoding engine if (glb.decode_ole > 0) { MIME_decode_OLE( unpackdir, hinfo, 0 ); } #endif // If our content-type was TNEF, then we need to decode it // using an external decoding engine (see tnef/tnef.c) // Admittingly, this is not the most ideal way of picking out // TNEF files from our decoded attachments, but, for now // it'll have to do, besides, it does work fine, without any // side-effects if (hinfo->content_type == _CTYPE_TNEF) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding TNEF format\n",FL); glb.attachment_count++; MIME_decode_TNEF( unpackdir, hinfo, 0 ); } // Decode TNEF // Look for Microsoft MHT files... and try decode them. // MHT files are just embedded email files, except they're usually // encoded as BASE64... so, you have to -unencode- them, to which // you find out that lo, you have another email. if ((strstr(hinfo->filename,".mht"))||(strstr(hinfo->name,".mht")) ) { if (glb.decode_mht != 0) { // Patched 26-01-03: supplied by Chris Hine if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Microsoft MHT format email filename='%s'\n",FL, hinfo->filename); snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename); // 20040305-1304:PLD: unpack the file, propagate result upwards result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss ); } } // Decode MHT files } // If result != -1 // End. MIME_generate_multiple_hardlink_filenames(hinfo,unpackdir); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Done for filename = '%s'",FL,hinfo->filename); return result; } /*------------------------------------------------------------------------ Procedure: MIME_postdecode_cleanup ID:1 Purpose: Performs any cleanup operations required after the immediate completion of the mailpack decoding. Input: char *unpackdir - directory where the mailpack was unpacked to Output: none Errors: ------------------------------------------------------------------------*/ int MIME_postdecode_cleanup( char *unpackdir, struct SS_object *ss ) { char fullpath[256]; int result; result = 0; do { char *filename; if (MIME_DNORMAL) LOGGER_log("%s:%d: Popping file...",FL); filename = SS_pop(ss); if (filename == NULL) break; if (MIME_DNORMAL) LOGGER_log("%s:%d: Popped file '%s'",FL, filename); if ( strncmp( glb.blankfileprefix, filename, strlen( glb.blankfileprefix ) ) == 0 ) { snprintf( fullpath, sizeof(fullpath), "%s/%s", unpackdir, filename ); result = unlink( fullpath ); if (MIME_VERBOSE) { if (result == -1) LOGGER_log("Error removing '%s'; %s", fullpath, strerror(errno)); else LOGGER_log("Removed %s [status = %d]\n", fullpath, result ); } } } while (1); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_multipart Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *hinfo, 4. int current_recursion_level, 5. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { int result = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Decoding multipart/embedded \n",FL); // If there is no filename, then we have a "standard" // embedded message, which can be just read off as a // continuous stream (simply with new boundaries // if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' )) { char *p; // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it // (ie, it's just the existing email with a new set of headers around it // then rather than saving it to a file, we'll just peel off these outter // layer of headers and get into the core of the message. This is why we // call unpack_stage2() because this function just reads the next set of // headers and decodes. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); p = BS_top(); if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary)); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (result == 0) { // Because we're calling MIME_unpack_single again [ie, recursively calling it // we need to now adjust the input-filename so that it correctly is prefixed // with the directory we unpacked to. snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level, ss ); } } // else-if transfer-encoding != B64 && filename was empty if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: done handling '%s' result = %d",FL,h->filename, result); return result; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_rfc822 Returns Type : int ----Parameter List 1. FFGET_FILE *f, Input stream 2. char *unpackdir, Directory to write files to 3. struct MIMEH_header_info *hinfo, Header information structure 4. int current_recursion_level, Current recursion level 5. struct SS_object *ss , String stack containing already decoded file names ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { /** Decode a RFC822 encoded stream of data from *f **/ int result = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Decoding RFC822 message\n",FL); /** If there is no filename, then we have a "standard" ** embedded message, which can be just read off as a ** continuous stream (simply with new boundaries **/ DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Filename='%s', encoding = %d",FL, h->filename, h->content_transfer_encoding); // if ((0)&&( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '0' )) 20041217-1635:PLD: if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' )) { /** Handle a simple plain text wrapped RFC822 email with no encoding applied to it **/ char *p; // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it // (ie, it's just the existing email with a new set of headers around it // then rather than saving it to a file, we'll just peel off these outter // layer of headers and get into the core of the message. This is why we // call unpack_stage2() because this function just reads the next set of // headers and decodes. DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); p = BS_top(); if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary)); } else { /** ...else... if the section has a filename or B64 type encoding, we need to put it through extra decoding **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Result of extracting %s is %d",FL,h->filename, result); if (result == 0) { /** Because we're calling MIME_unpack_single again [ie, recursively calling it we need to now adjust the input-filename so that it correctly is prefixed with the directory we unpacked to. **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,h->filename); snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,scratch); result = MIME_unpack_single( unpackdir, scratch, current_recursion_level, ss ); result = 0; } } /** else-if transfer-encoding != B64 && filename was empty **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: done handling '%s' result = %d",FL,h->filename, result); return result; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_plain Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *h, 4. int current_recursion_level, 5. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_plain( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { /** Handle a plain text encoded data stream from *f **/ int result = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_plain:DEBUG: Handling plain email",FL); result = MIME_decode_encoding( f, unpackdir, h, ss ); if ((result == MIME_ERROR_FFGET_EMPTY)||(result == 0)) { /** Test for RFC822 content... if so, go decode it **/ snprintf(h->scratch,sizeof(h->scratch),"%s/%s",unpackdir,h->filename); if (MIME_is_file_RFC822(h->scratch)==1) { /** If the file is RFC822, then decode it using MIME_unpack_single() **/ if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(glb.header_longsearch); result = MIME_unpack_single( unpackdir, h->scratch, current_recursion_level, ss ); if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(0); } } return result; } /*------------------------------------------------------------------------ Procedure: MIME_unpack_stage2 ID:1 Purpose: This function commenced with the file decoding of the attachments as required by the MIME structure of the file. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss ) { int result = 0; struct MIMEH_header_info *h; char *p; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Start, recursion %d\n",FL, current_recursion_level); if (current_recursion_level > glb.max_recursion_level) { /** Test for recursion limits **/ if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_unpack_stage2:WARNING: Current recursion level of %d is greater than permitted %d"\ ,FL, current_recursion_level, glb.max_recursion_level); return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040306-1301:PLD } h = hinfo; // Get our headers and determin what we have... // if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing headers (initial)\n",FL); // Parse the headers, extracting what information we need /** 20041216-1102:PLD: Keep attempting to read headers until we get a sane set **/ do { /** Read next set of headers, repeat until a sane set of headers are found **/ result = MIMEH_parse_headers(f,h); DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing of headers done, sanity = %d, result = %d",FL,h->sanity, result); } while ((h->sanity == 0)&&(result != -1)); DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Repeat loop of header-reading done, sanity = %d, result = %d",FL,h->sanity, result); glb.header_defect_count += MIMEH_get_defect_count(h); // Test the result output switch (result) { case -1: return MIME_ERROR_FFGET_EMPTY; break; } // Copy the subject over to our global structure if the subject is not already set. // this will give us the 'main' subject of the entire email and prevent // and subsequent subjects from clobbering it. //if (glb.subject[0] == '\0') snprintf(glb.subject, _MIME_STRLEN_MAX, "%s", h->subject ); if ((strlen(glb.subject) < 1)&&(h->subject != NULL)&&(strlen(h->subject) > 0)) { snprintf(glb.subject, sizeof(glb.subject), "%s", h->subject ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Headers parsed, Result = %d, Boundary located = %d\n"\ ,FL,result, hinfo->boundary_located); // Test to see if we encountered any DoubleCR situations while we // were processing the headers. If we did encounter a doubleCR // then we need to decode that data and then reset the doubleCR // flag. if (MIMEH_get_doubleCR() != 0) { MIME_doubleCR_decode( MIMEH_get_doubleCR_name(), unpackdir, h, current_recursion_level); MIMEH_set_doubleCR( 0 ); FFGET_SDL_MODE = 0; } // If we located a boundary being specified (as apposed to existing) // then we push it to the BoundaryStack if (h->boundary_located) { char *lbc; char *fbc; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Boundary located, pushing to stack (%s)\n",FL,h->boundary); BS_push(h->boundary); // Get the first and last character positions of the boundary // so we can test these for quotes. fbc = h->boundary; lbc = h->boundary +strlen(h->boundary) -1; // Circumvent attempts to trick the boundary // Even though we may end up pushing 3 boundaries to the stack // they will be popped off if they're not needed. if (*fbc == '"') { BS_push((fbc +1)); MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); } if (*lbc == '"') { *lbc = '\0'; BS_push(h->boundary); *lbc = '"'; MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); } h->boundary_located = 0; } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding in BOUNDARY-LESS mode\n",FL); if (h->content_type == _CTYPE_RFC822) { // Pass off to the RFC822 handler if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with RFC822 decoder\n",FL); result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss); } else if (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type)) { // Pass off to the multipart handler if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with Multipart decoder\n",FL); result = MIME_handle_multipart(f,unpackdir,h,current_recursion_level,ss); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding boundaryless file (%s)...\n",FL,h->filename); result = MIME_handle_plain( f, unpackdir,h,current_recursion_level,ss); } // else-if content was RFC822 or multi-part return result; } // End of the boundary-LESS mode ( processing the mail which has no boundaries in the primary headers ) if ((BS_top()!=NULL)&&(result == 0)) { // Commence decoding WITH boundary awareness. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with boundaries (filename = %s)\n",FL,h->filename); // Decode the data in the current MIME segment // based on the header information retrieved from // the start of this function. result = MIME_decode_encoding(f, unpackdir, h, ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Done decoding, result = %d",FL,result); if (result == 0) { if (BS_top()!=NULL) { // As this is a multipart email, then, each section will have its // own headers, so, we just simply call the MIMEH_parse call again // and get the attachment details while ((result == 0)||(result == MIME_STATUS_ZERO_FILE)) { h->content_type = -1; h->filename[0] = '\0'; h->name[0] = '\0'; h->content_transfer_encoding = -1; h->content_disposition = -1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding headers...\n",FL); do { result = MIMEH_parse_headers(f,h); } while ((h->sanity == 0)&&(result != -1)); glb.header_defect_count += MIMEH_get_defect_count(h); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Mime header parsing result = %d\n",FL, result); // 20040305-1331:PLD if (result == -1) { result = MIME_ERROR_FFGET_EMPTY; return result; } if (h->boundary_located) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Pushing boundary %s\n",FL, h->boundary); BS_push(h->boundary); h->boundary_located = 0; } if (result == _MIMEH_FOUND_FROM) { return _MIMEH_FOUND_FROM; } if (result == 0) { // If we locate a new boundary specified, it means we have a // embedded message, also if we have a ctype of RFC822 if ( (h->boundary_located) \ || (h->content_type == _CTYPE_RFC822)\ || (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type))\ ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Multipart/RFC822 mail headers found\n",FL); /* If there is no filename, then we have a "standard" * embedded message, which can be just read off as a * continuous stream (simply with new boundaries */ if (( h->content_type == _CTYPE_RFC822 )) { // If the content_type is set to message/RFC822 // then we simply read off the data to the next // boundary into a seperate file, then 'reload' // the file into ripMIME. Certainly, this is not // the most efficent way of dealing with nested emails // however, it is a rather robust/reliable way. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Chose Content-type == RFC822 clause",FL); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Calling MIME_decode_encoding()",FL); result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss); // First up - extract the RFC822 body out of the parent mailpack // XX result = MIME_decode_encoding( f, unpackdir, h, ss ); // Now decode the RFC822 body. /** XX if (result == 0) { snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now calling MIME_unpack_single() on the file '%s' for our RFC822 decode operation.",FL, scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack( unpackdir, h->filename, current_recursion_level +1 ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Unpack result = %d", FL, result); result = 0; } else return result; // 20040305-1312:PLD */ } else if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&(h->filename[0] == '\0' )) { // Decode nameless MIME segments which are not BASE64 encoded // // To be honest, i've forgotten what this section of test is supposed to pick out // in terms of files - certainly it does pick out some, but I'm not sure what format // they are. Shame on me for not remembering, in future I must comment more. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: NON-BASE64 DECODE\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); // When we've exited from decoding the sub-mailpack, we need to restore the original // boundary p = BS_top(); if (p) snprintf(h->boundary,_MIME_STRLEN_MAX,"%s",p); /** 20041207-0106:PLD: ** changed to test for _INLINE **/ //} else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) { } else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) { // AppleDouble needs to be handled explicity - as even though it's // and embedded format, it does not have the normal headers->[headers->data] // layout of other nested emails if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Handle Appledouble explicitly",FL); result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: RFC822 Message to be decoded...\n",FL); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (result != 0) return result; // 20040305-1313:PLD else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now running ripMIME over decoded RFC822 message...\n",FL); // Because we're calling MIME_unpack_single again [ie, recursively calling it // we need to now adjust the input-filename so that it correctly is prefixed // with the directory we unpacked to. snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level,ss ); } } // else-if transfer-encoding wasn't B64 and filename was blank } else { // If the attachment included in this MIME segment is NOT a // multipart or RFC822 embedded email, we can then simply use // the normal decoding function to interpret its data. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment \n",FL); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment '%s' done. \n",FL, h->filename); // See if we have an attachment output which is actually another // email. // // Added 24 Aug 2003 by PLD // Ricardo Kleemann supplied offending mailpack to display // this behavior if (result != 0) return result; // 20040305-1314:PLD else { char *mime_fname; mime_fname = PLD_dprintf("%s/%s", unpackdir, h->filename); if(mime_fname != NULL) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Testing '%s' for email type",FL,mime_fname); if (MIME_is_file_RFC822(mime_fname)) { //MIME_unpack_single( unpackdir, mime_fname, (hinfo->current_recursion_level+1), ss); MIME_unpack_single( unpackdir, mime_fname, current_recursion_level+1,ss); } free(mime_fname); } } } // if there was a boundary, RFC822 content or it was multi-part } else { // if the result is not 0 break; } // result == 0 test } // While (result) // ???? Why do we bother to pop the stack ??? // The way BS is designed it will auto-pop the inner nested boundaries // when a higher-up one is located. //if (result == 0) BS_pop(); // 20040305-2219:PLD } // if MIME_BS_top() } // if result == 0 } // if (result) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Exiting with result=%d recursion=%d\n",FL,result, current_recursion_level); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_mailbox ID:1 Purpose: Decodes mailbox formatted email files Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ) { FFGET_FILE f; FILE *fi; FILE *fo; char fname[1024]; char line[1024]; int mcount=0; int lastlinewasblank=1; int result; int input_is_stdin=0; snprintf(fname,sizeof(fname),"%s/tmp.email000.mailpack",unpackdir); if ((mpname[0] == '-')&&(mpname[1] == '\0')) { fi = stdin; input_is_stdin=1; } else { // fi = fopen(mpname,"r"); if (strcmp(mpname,"-")==0) fi = stdin; else fi = fopen(mpname,"r"); // 20040208-1715:PLD if (!fi) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for reading (%s)",FL, mpname,strerror(errno)); return -1; } } fo = fopen(fname,"w"); if (!fo) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for writing (%s)",FL, fname,strerror(errno)); return -1; } FFGET_setstream(&f, fi); while (FFGET_fgets(line,1024,&f)) { // If we have the construct of "\n\rFrom ", then we // can be -pretty- sure that a new email is about // to start if ((lastlinewasblank==1)&&(strncasecmp(line,"From ",5)==0)) { // Close the mailpack fclose(fo); // Now, decode the mailpack //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss); // 20040317-2358:PLD MIME_unpack_single(unpackdir, fname, current_recursion_level ,ss ); // Remove the now unpacked mailpack result = remove(fname); if (result == -1) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno)); } // Create a new mailpack filename, and keep on going... snprintf(fname,sizeof(fname),"%s/tmp.email%03d.mailpack",unpackdir,++mcount); fo = fopen(fname,"w"); } else { fprintf(fo,"%s",line); } // If the line is blank, then note this down because // if our NEXT line is a From, then we know that // we have reached the end of the email // if ((line[0] == '\n') || (line[0] == '\r')) { lastlinewasblank=1; } else lastlinewasblank=0; } // While fgets() // Don't attempt to close STDIN if that's where the mailpack/mailbox // has come from. Although this should really cause problems, // it's better to be safe than sorry. if (input_is_stdin == 0) { fclose(fi); } // Now, even though we have run out of lines from our main input file // it DOESNT mean we dont have some more decoding to do, in fact // quite the opposite, we still have one more file to decode // Close the mailpack fclose(fo); // Now, decode the mailpack //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss); // 20040317-2358:PLD MIME_unpack_single(unpackdir, fname, current_recursion_level , ss ); // Remove the now unpacked mailpack result = remove(fname); if (result == -1) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_unpack_single Returns Type : int ----Parameter List 1. char *unpackdir, 2. char *mpname, 3. int current_recursion_level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ) { FILE *fi; /* Pointer for the MIME file we're going to be going through */ int result = 0; if (current_recursion_level > glb.max_recursion_level) { LOGGER_log("%s:%d:MIME_unpack_single:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level); return MIME_ERROR_RECURSION_LIMIT_REACHED; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: dir=%s packname=%s level=%d (max = %d)\n",FL, unpackdir, mpname, current_recursion_level, glb.max_recursion_level); /* if we're reading in from STDIN */ if( mpname[0] == '-' && mpname[1] == '\0' ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: STDIN opened...\n",FL); fi = stdin; } else { fi = fopen(mpname,"r"); if (!fi) { LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Cannot open file '%s' for reading.\n",FL, mpname); return -1; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Input file (%s) opened...\n",FL, mpname); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Checking input streams...\n",FL); /* check to see if we had problems opening the file */ if (fi == NULL) { LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Could not open mailpack file '%s' (%s)",FL, mpname, strerror(errno)); return -1; } // 20040317-2359:PLD result = MIME_unpack_single_fp(unpackdir,fi,current_recursion_level , ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: result = %d, recursion = %d, filename = '%s'", FL, result, current_recursion_level, mpname ); if ((current_recursion_level > 1)&&(result == 241)) result = 0; fclose(fi); return result; } /*------------------------------------------------------------------------ Procedure: MIME_unpack_single ID:1 Purpose: Decodes a single mailpack file (as apposed to mailbox format) into its possible attachments and text bodies Input: char *unpackdir: Directory to unpack the attachments to char *mpname: Name of the mailpack we have to decode int current_recusion_level: Level of recursion we're currently at. Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss ) { struct MIMEH_header_info h; int result = 0; int headers_save_set_here = 0; FFGET_FILE f; FILE *hf = NULL; // Because this MIME module gets used in both CLI and daemon modes // we should check to see that we can report to stderr // if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: dir=%s level=%d (max = %d)\n",FL, unpackdir, current_recursion_level, glb.max_recursion_level); if (current_recursion_level > glb.max_recursion_level) { LOGGER_log("%s:%d:MIME_unpack_single_fp:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level); // return -1; return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040305-1302:PLD //return 0; // 20040208-1723:PLD } else h.current_recursion_level = current_recursion_level; glb.current_line = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: recursion level checked...%d\n",FL, current_recursion_level); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: DumpHeaders = %d\n",FL, glb.save_headers); if ((!hf)&&(glb.save_headers)&&(MIMEH_get_headers_save()==0)) { // Prepend the unpackdir path to the headers file name snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, glb.headersname); hf = fopen(scratch,"w"); if (!hf) { glb.save_headers = 0; LOGGER_log("%s:%d:MIME_unpack_single_fp:ERROR: Cannot open '%s' for writing (%s)",FL, glb.headersname,strerror(errno)); } else { headers_save_set_here = 1; MIMEH_set_headers_save(hf); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Setting up streams to decode\n",FL); FFGET_setstream(&f, fi); /** Initialize the header record **/ h.boundary[0] = '\0'; h.boundary_located = 0; h.filename[0] = '\0'; h.name[0] = '\0'; h.content_transfer_encoding = -1; h.content_disposition = -1; h.content_type = -1; h.x_mac = 0; SS_init(&(h.ss_filenames)); SS_init(&(h.ss_names)); if (MIME_DNORMAL) { SS_set_debug(&(h.ss_filenames), 1); SS_set_debug(&(h.ss_names), 1); } if (glb.verbose_defects) { int i; for (i = 0; i < _MIMEH_DEFECT_ARRAY_SIZE; i++) { h.defects[i] = 0; } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: preparing to decode, calling stage2...\n",FL); // 20040318-0001:PLD result = MIME_unpack_stage2(&f, unpackdir, &h, current_recursion_level +1, ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: done decoding ( in stage2 ) result=%d, to %s\n",FL, result, unpackdir); // fclose(fi); 20040208-1726:PLD if ( headers_save_set_here > 0 ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Closing header file.\n",FL); fflush(stdout); MIMEH_set_headers_nosave(); fclose(hf); } if (glb.verbose_defects) MIMEH_dump_defects(&h); /** Flush out the string stacks **/ SS_done(&(h.ss_filenames)); SS_done(&(h.ss_names)); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Done. Result=%d Recursion=%d\n",FL, result, current_recursion_level); return result; // return status; // 20040305-1318:PLD } /*------------------------------------------------------------------------ Procedure: MIME_unpack ID:1 Purpose: Front end to unpack_mailbox and unpack_single. Decides which one to execute based on the mailbox setting Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack( char *unpackdir, char *mpname, int current_recursion_level ) { int result = 0; struct SS_object ss; // Stores the filenames that are created in the unpack operation if (current_recursion_level > glb.max_recursion_level) return MIME_ERROR_RECURSION_LIMIT_REACHED; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking %s to %s, recursion level is %d",FL,mpname,unpackdir,current_recursion_level); MIMEH_set_outputdir(unpackdir); if (MIME_DNORMAL) SS_set_debug(&ss,1); SS_init(&ss); if (glb.mailbox_format > 0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking using mailbox format",FL); result = MIME_unpack_mailbox( unpackdir, mpname, (current_recursion_level), &ss ); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking standard mailpack",FL,mpname,unpackdir,current_recursion_level); result = MIME_unpack_single( unpackdir, mpname, (current_recursion_level +1), &ss ); } if (glb.no_nameless) { MIME_postdecode_cleanup( unpackdir, &ss ); } if (MIME_DNORMAL) { LOGGER_log("%s:%d:MIME_unpack: Files unpacked from '%s' (recursion=%d);",FL,mpname,current_recursion_level); SS_dump(&ss); } SS_done(&ss); if (MIME_DNORMAL) SS_set_debug(&ss,1); // If the result is a non-critical one (ie, just running out of data from the // file we attempted to decode - then don't propergate it, on the other switch (result) { case 1: result = 0; break; case MIME_STATUS_ZERO_FILE: result = 0; break; case MIME_ERROR_FFGET_EMPTY: case MIME_ERROR_RECURSION_LIMIT_REACHED: result = 0; break; default: break; } if (current_recursion_level == 0) { //LOGGER_log("%s:%d:MIME_unpack:DEBUG: Clearing boundary stack",FL); BS_clear(); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking of %s is done.",FL,mpname); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: -----------------------------------",FL); return result; } /*-------------------------------------------------------------------- * MIME_close * * Closes the files used in MIME_unpack, such as headers etc */ int MIME_close( void ) { if (headers) { fclose(headers); } return 0; } /*----------END OF MIME.c------------*/ p3scan-2.3.2/ripmime-1.4.0.6/mime.h0000644000175000001440000000740510347164530015120 0ustar jlaiusers #ifndef LIBMIME #define LIBMIME /* MIME.h */ #define LIBMIME_VERSION "200511170923" /* Exit Error codes */ #define _EXITERR_UNDEFINED_BOUNDARY 100 #define _EXITERR_PRINT_QUOTABLE_INPUT_NOT_OPEN 200 #define _EXITERR_PRINT_QUOTABLE_OUTPUT_NOT_OPEN 201 #define _EXITERR_BASE64_OUTPUT_NOT_OPEN 210 #define _EXITERR_BASE64_UNABLE_TO_OUTPUT 211 #define _EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT 220 #define _EXITERR_MIMEREAD_CANNOT_WRITE_OUTPUT 221 #define _EXITERR_MIMEUNPACK_CANNOT_OPEN_INPUT_FILE 230 #define _EXITERR_MIMEUNPACK_CANNOT_OPEN_HEADERS_FILE 231 #define MIME_ERROR_RECURSION_LIMIT_REACHED 240 #define MIME_ERROR_FFGET_EMPTY 241 #define MIME_ERROR_B64_INPUT_STREAM_EOF 242 #define _MIME_RENAME_METHOD_INFIX 1 #define _MIME_RENAME_METHOD_PREFIX 2 #define _MIME_RENAME_METHOD_POSTFIX 3 #define _MIME_STRLEN_MAX 1023 /* Debug levels */ #define _MIME_DEBUG_PEDANTIC 10 #define _MIME_DEBUG_NORMAL 1 /* Filename Encoding characters */ #define MIME_ISO_ENCODING_Q 'Q' #define MIME_ISO_ENCODING_q 'q' #define MIME_ISO_ENCODING_B 'B' #define MIME_ISO_ENCODING_b 'b' #define MIME_BLANKZONE_SAVE_TEXTFILE 4 #define MIME_BLANKZONE_SAVE_FILENAME 8 /* Quoted-Printable decoding modes */ #define MIME_QPMODE_STD 0 #define MIME_QPMODE_ISO 1 /* BASE64 decode status return codes */ #define MIME_BASE64_STATUS_HIT_BOUNDARY 1 #define MIME_BASE64_STATUS_ZERO_FILE 2 #define MIME_BASE64_STATUS_OK 0 /* status return codes */ #define MIME_STATUS_ZERO_FILE 100 int MIME_version( void ); int MIME_read( char *mpname ); /* returns filesize in KB */ int MIME_unpack( char *unpackdir, char *mpname, int current_recusion_level ); //int MIME_unpack_single( char *unpackdir, char *mpname, int current_recusion_level ); //int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recusion_level ); //int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level ); int MIME_insert_Xheader( char *fname, char *xheader ); int MIME_set_blankfileprefix( char *prefix ); int MIME_set_recursion_level(int level); int MIME_set_verbosity( int level ); int MIME_set_verbosity_contenttype( int level ); int MIME_set_verbosity_12x_style( int level ); int MIME_set_verbose_defects( int level ); int MIME_set_report_MIME( int level ); int MIME_set_quiet( int level ); int MIME_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ); int MIME_set_debug( int level ); int MIME_set_dumpheaders( int level ); int MIME_set_headersname( char *fname ); int MIME_set_decode_uudecode( int level ); int MIME_set_decode_tnef( int level ); int MIME_set_decode_ole( int level ); int MIME_set_decode_qp( int level ); int MIME_set_decode_base64( int level ); int MIME_set_decode_doubleCR( int level ); int MIME_set_decode_mht( int level ); int MIME_set_header_longsearch( int level ); int MIME_set_blankzone_save_option( int option ); int MIME_set_blankzone_filename( char *filename ); char *MIME_get_blankzone_filename( void ); int MIME_set_syslogging( int level ); int MIME_set_stderrlogging( int level ); int MIME_set_no_nameless( int level ); int MIME_set_uniquenames( int level ); int MIME_set_renamemethod( int method ); int MIME_set_paranoid( int level ); int MIME_set_mailboxformat( int level ); int MIME_set_webform( int level ); int MIME_get_attachment_count( void ); int MIME_set_name_by_type( int level ); int MIME_set_multiple_filenames( int level ); int MIME_get_header_defect_count( void ); int MIME_is_file_mime( char *fname ); int MIME_is_file_uuenc( char *fname ); char *MIME_get_blankfileprefix( void ); char *MIME_get_headersname( void ); char *MIME_get_subject( void ); int MIME_init( void ); int MIME_close( void ); int MIME_set_tmpdir( char *tmpdir ); //int MIME_postdecode_cleanup( char *unpackdir, struct SS_object *ss ); //int MIME_decode_TNEF( FILE *f, char *unpackdir, struct _header_info *hinfo, int keep ); #endif p3scan-2.3.2/ripmime-1.4.0.6/rawget.c0000644000175000001440000000162210347164530015450 0ustar jlaiusers#include #include int RAWGET_get( unsigned char *buffer, int max, FILE *f ) { unsigned char c; // read buffer int count = 0; // How many bytes read // Special situation here, if we have a return from MIME_headers which indicates // that we have data in a MIMEH_pushback, then we need to process that first, before we // go back into the data file. // // Whilst we've got less bytes than the maximum availabe // for the buffer, we keep on reading // while (count < max) { // If we do infact read in 1 bytes... if (fread(&c,1,1,f)==1) { *buffer = c; // Set the byte in the buffer buffer++; // move the buffer pointer count++; // Increment the byte cound if (c == '\n') // If we encounter a \n (or \r) { break; // Hop out of while loop } } else break; // if we didn't read right, then jump out as well } return count; } p3scan-2.3.2/ripmime-1.4.0.6/rawget.h0000644000175000001440000000007410347164530015455 0ustar jlaiusersint RAWGET_get( unsigned char *buffer, int max, FILE *f ); p3scan-2.3.2/ripmime-1.4.0.6/ripmime.10000644000175000001440000001223310347164530015537 0ustar jlaiusers.\" ripMIME is a simple program which provides extensive email attachment extraction abilities. .TH "ripMIME" "1" "1.4.0.1" "Paul L Daniels" "MIME decoders" .SH "NAME" ripMIME \- email attachment / decomposition tool. .SH "SYNTAX" ripMIME \-i \-d .br [\-p prefix][\-e [headerfile]] .br [\-v][\-q][\-\-verbose\-contenttype][\-\-verbose\-oldstyle][\-\-verbose\-defects][\-\-stdout][\-\-stderr][\-\-syslog] .br [\-\-paranoid] .br [\-\-name\-by\-type][\-\-no\-nameless][\-\-overwrite][\-\-no_nameless] .br [\-\-unique_names[\-\-prefix|\-\-postfix|\-\-infix]][\-\-mailbox] .br [\-\-no\-tnef][\-\-no\-quotedprintable][\-\-no\-uudecode][\-\-no\-ole][\-\-no\-doublecr][\-\-no\-mht] .br [\-\-disable\-qmail\-bounce][\-\-recursion\-max ] .br [\-\-no\-multiple\-filenames] .br [\-\-exteded\-errors][\-\-debug][\-\-version|\-V][\-\-buildcodes][\-h] .br .SH "DESCRIPTION" ripMIME is a command line tool used to aid in the extraction of email borne attachments to files which can be processed using other UNIX tools. ripMIME supports both the RFC MIME standards as well as being able to behave according to various MUA 'features' which are often used as exploitation holes. .SH "OPTIONS" .LP .TP \-i Input MIME encoded file (use '\-' to input from STDIN) .TP \-d Output directory .TP \-p Specify prefix filename to be used on files without a filename (default 'text') .TP \-e [headers file name] Dump headers from mailpack (default '_headers_') .TP \-v Turn on verbosity .TP \-q Run quietly, do not report non\-fatal errors .TP \-\-verbose\-contenttype Turn on verbosity of file content type .TP \-\-verbose\-oldstyle Uses the v1.2.x style or filename reporting .TP \-\-verbose\-defects Report MIME header/body defects located in the mailpack .TP \-\-stdout All reporting goes to stdout (Default) .TP \-\-stderr All reporting goes to stderr .TP \-\-syslog All reporting goes to syslog .TP \-\-no\-paranoid [ Deprecated ] Turns off strict ascii\-alnum filenaming .TP \-\-paranoid Converts all filenames to strict 7\-bit compliance .TP \-\-name\-by\-type Saves a given attachment by its content\-type if it has no other name .TP \-\-no\-nameless Do not save nameless attachments .TP \-\-overwrite Overwrite files if they have the same name on extraction .TP \-\-unique\-names Dont overwrite existing files (This is the default behaviour) .TP \-\-prefix rename by putting unique code at the front of the filename .TP \-\-postfix rename by putting unique code at the end of the filename .TP \-\-infix rename by putting unique code in the middle of the filename .TP \-\-recursion\-max Set the maximum recursion level into a mailpack. Often emails are forwarded copies of an existing email, each time this is done a new recursion level is required. Malicious emails can be constructed with many hundereds of recursion levels to induce stack faults in decoding programs. ripMIME is hard coded with a default of 20 levels, this may be overidden using this parameter. .TP \-\-mailbox Process mailbox file .TP \-\-extended\-errors Returns error codes for non\-fatal decoding situations .TP \-\-debug Produces detailed information about the whole decoding process .SS "Attachment type decoding switches" .TP \-\-no\-tnef Turn off TNEF/winmail.dat decoding .TP \-\-no\-ole Turn off OLE decoding .TP \-\-no\-uudecode Turns off the facility of detecting UUencoded attachments in emails .TP \-\-no\-quotedprintable Turns off the facility of decoding QuotedPrintable data .TP \-\-no\-doublecr Turns off saving of double\-CR embedded data .TP \-\-no\-mht Turns off MHT (a Microsoft mailpack attachment format ) decoding .TP \-\-disable\-qmailbounce Turns off ripMIME's look\-ahead searching for RFC822 headers within a body of text. Normally the look\-ahead is useful for decoding embedded emails which have been bounced back by systems like qmail, but have been included in such a way that they are not meant to be decoded, unfortunately some MUA (Outlook for one) still decode it, hence we need to by default check for attachments in such forwarded bodies. .TP \-\-no\-multiple\-filenames Turns off the behaviour of handling multiple filename occurances in a single MIME header. Normally ripMIME will generate a hard link for each additional filename beyond the original attachment filename detected. .SS Auxillary parameters .TP \-\-buildcodes Displays the information obtained by the Makefile script when ripMIME was built. This includes the Unix timestamp, human readable version of the timestamp and the output from 'uname \-a'. .TP \-V \-\-version Give version information .TP \-h Terse information on how to use ripMIME. .SH "FILES" .I None .br .SH "ENVIRONMENT VARIABLES" .TP .I None .SH "EXAMPLES" .LP To unpack an email in a file 'mailpack' to the directory /tmp with verbose output of the files unpacked; .LP ripmime \-i mailpack \-v \-d /tmp .LP .SH "AUTHORS" Paul L Daniels .br ripMIME WWW site http://www.pldaniels.com/ripmime .br ripMIME mailing list .br For mailpacks which do not appear to decode correctly \- please email to .SH "SEE ALSO" .BR altermime (1), .BR inflex (1), .BR ripole (1), .BR opentnef (1) p3scan-2.3.2/ripmime-1.4.0.6/ripmime.c0000644000175000001440000005066710347164530015636 0ustar jlaiusers/*---------------------------------------- * ripMIME - * * Written by Paul L Daniels * pldaniels@pldaniels.com * * (C)2001 P.L.Daniels * http://www.pldaniels.com/ripmime * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "buildcodes.h" #include "logger.h" #include "ffget.h" #include "mime.h" #include "strstack.h" #include "MIME_headers.h" #define RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR 1 #define RIPMIME_ERROR_CANT_OPEN_INPUT_FILE 2 #define RIPMIME_ERROR_NO_INPUT_FILE 3 #define RIPMIME_ERROR_INSUFFICIENT_PARAMETERS 4 #define RIPMIME_ERROR_TIMEOUT 5 struct RIPMIME_globals { char *inputfile; char *dir; int use_return_codes; int timeout; int quiet; int verbose_defects; int verbose; }; //-b [blankzone file name] : Dump the contents of the MIME blankzone to 'filename' char defaultdir[] = "."; struct RIPMIME_globals *ripmime_globals = NULL; char version[] = "v1.4.0.6 - December 12, 2005 (C) PLDaniels http://www.pldaniels.com/ripmime"; char help[] = "ripMIME -i -d " "[-p prefix] [-e [header file]] [-vVh] [--version]" "[--no_nameless] [--unique_names [--prefix|--postfix|--infix]]" "[--paranoid] [--mailbox] [--formdata] [--debug]" "[--no-tnef] [--no-quotedprintable] [--no-uudecode]\n" "Options available :\n" "-i : Input MIME encoded file (use '-' to input from STDIN)\n" "\tIf is a directory, it will be recursed\n" "-d : Output directory\n" "-p : Specify prefix filename to be used on files without a filename (default 'text')\n" "-e [headers file name] : Dump headers from mailpack (default '_headers_')\n" "-v : Turn on verbosity\n" "-q : Run quietly, do no report non-fatal errors\n" "\n" "--verbose-contenttype : Turn on verbosity of file content type\n" "--verbose-oldstyle : Uses the v1.2.x style or filename reporting\n" "--verbose-defects: Display a summary of defects in the email\n" "--verbose-mime: Displays 'Email is MIME' if the email is MIME\n" "--stdout : All reporting goes to stdout (Default)\n" "--stderr : All reporting goes to stderr\n" "--syslog : All reporting goes to syslog\n" "\n" "--no-paranoid : [ Deprecated ] Turns off strict ascii-alnum filenaming\n" "--paranoid: Converts all filenames to strict 7-bit compliance\n" "--name-by-type: Saves a given attachment by its content-type if it has no other name\n" "--no-nameless : Do not save nameless attachments\n" "--overwrite : Overwrite files if they have the same name on extraction\n" "--unique-names : Dont overwrite existing files (This is the default behaviour)\n" "--prefix : rename by putting unique code at the front of the filename\n" "--postfix : rename by putting unique code at the end of the filename\n" "--infix : rename by putting unique code in the middle of the filename\n" "\n" "--mailbox : Process mailbox file\n" "--formdata : Process as form data (from HTML form etc)\n" "\n" "--no-tnef : Turn off TNEF/winmail.dat decoding\n" "--no-ole : Turn off OLE decoding\n" "--no-uudecode : Turns off the facility of detecting UUencoded attachments in emails\n" "--no-quotedprintable : Turns off the facility of decoding QuotedPrintable data\n" "--no-doublecr : Turns off saving of double-CR embedded data\n" "--no-mht : Turns off MHT (a Microsoft mailpack attachment format ) decoding\n" "--no-multiple-filenames : Turns off the multiple filename exploit handling\n" "\n" "--disable-qmail-bounce : Turns off qmail bounced email testing\n" "--recursion-max : Set the maximum recursion level to 'level'\n" "--timeout : Set the maximum number of CPU seconds ripMIME can run for\n" "\n" "--debug : Produces detailed information about the whole decoding process\n" "--extended-errors: Produces more return codes, even for non-fatals\n" "-V --version : Give version information\n" "--buildcodes : Give the build information (tstamp, date and system information)\n" "-h : This message (help)\n\n\n"; /*-----------------------------------------------------------------\ Function Name : RIPMIME_report_filename_decoded Returns Type : int ----Parameter List 1. char *filename, 2. char *contenttype, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_report_filename_decoded (char *filename, char *contenttype) { char *p; p = strrchr(filename,'/'); if (!p) p = filename; else p++; if (contenttype != NULL) { LOGGER_log ("Decoding content-type=%s filename=%s", contenttype, p); } else { LOGGER_log ("Decoding filename=%s", p); } return 0; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_parse_parameters Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, 2. int argc, 3. char **argv, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_parse_parameters (struct RIPMIME_globals *glb, int argc, char **argv) { int i; int result = 0; MIME_set_filename_report_fn (RIPMIME_report_filename_decoded); for (i = 1; i < argc; i++) { // if the first char of the argument is a '-', then we possibly have a flag if (argv[i][0] == '-') { // test the 2nd char of the parameter switch (argv[i][1]) { case 'i': if (argv[i][2] != '\0') { glb->inputfile = &argv[i][2]; } else { i++; if (i < argc) { glb->inputfile = argv[i]; } else { LOGGER_log("ERROR: insufficient parameters after '-i'\n"); } } break; case 'd': if (argv[i][2] != '\0') { glb->dir = &(argv[i][2]); } else { i++; if (i < argc) { glb->dir = argv[i]; } else { LOGGER_log("ERROR: insufficient parameters after '-d'\n"); } } break; case 'p': if (argv[i][2] != '\0') { MIME_set_blankfileprefix (&argv[i][2]); } else { i++; if (i < argc) { MIME_set_blankfileprefix (argv[i]); } else { LOGGER_log("ERROR: insufficient parameters after '-p'\n"); } } break; // this is in mime.h case 'e': MIME_set_dumpheaders (1); if (argv[i][2] != '\0') { MIME_set_headersname (&argv[i][2]); } else { if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) MIME_set_headersname (argv[++i]); } break; // makes MIME dump out the headers to a file #ifdef RIPMIME_BLANKZONE case 'b': if (argv[i][2] != '\0') { MIME_set_blankzone_filename (&argv[i][2]); } else { if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) MIME_set_blankzone_filename (argv[++i]); } break; // blankzone storage option #endif case 'v': MIME_set_verbosity (1); glb->verbose = 1; break; case 'q': glb->quiet = 1; MIME_set_quiet(glb->quiet); break; case 'V': fprintf (stdout, "%s\n", version); exit (1); break; case 'h': fprintf (stdout, "%s\n", help); exit (1); break; // if we get ANOTHER - symbol, then we have an extended flag case '-': if (strncmp (&(argv[i][2]), "verbose-contenttype", strlen ("verbose-contenttype")) == 0) { MIME_set_verbosity_contenttype (1); /** 20051117-0927:PLD: ** If client uses --verbose-mime, then make ripMIME report when it ** detects a MIME encoded email **/ } else if (strncmp(&(argv[i][2]), "verbose-mime", strlen("verbose-mime"))==0) { MIME_set_report_MIME(1); } else if (strncmp (&(argv[i][2]), "verbose-oldstyle", strlen ("verbose-oldstyle")) == 0) { MIME_set_verbosity_12x_style (1); MIME_set_filename_report_fn (NULL); } else if (strncmp (&(argv[i][2]), "verbose-defects",15) == 0) { glb->verbose_defects = 1; MIME_set_verbose_defects(1); } else if (strncmp (&(argv[i][2]), "paranoid", 8) == 0) { MIME_set_paranoid (1); } else if (strncmp (&(argv[i][2]), "no_paranoid", 11) == 0) { MIME_set_paranoid (0); } else if (strncmp (&(argv[i][2]), "no-paranoid", 11) == 0) { MIME_set_paranoid (0); } else if (strncmp (&(argv[i][2]), "prefix", 6) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_PREFIX); } else if (strncmp (&(argv[i][2]), "postfix", 7) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_POSTFIX); } else if (strncmp (&(argv[i][2]), "infix", 5) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_INFIX); } else if (strncmp (&(argv[i][2]), "overwrite", 9) == 0) { MIME_set_uniquenames (0); } else if (strncmp (&(argv[i][2]), "unique_names", 12) == 0) { MIME_set_uniquenames (1); } else if (strncmp (&(argv[i][2]), "unique-names", 12) == 0) { MIME_set_uniquenames (1); } else if (strncmp(&(argv[i][2]), "name-by-type", 12) == 0) { MIME_set_name_by_type(1); } else if (strncmp (&(argv[i][2]), "syslog", 9) == 0) { LOGGER_set_output_mode (_LOGGER_SYSLOG); LOGGER_set_syslog_mode (LOG_MAIL | LOG_INFO); } else if (strncmp (&(argv[i][2]), "stderr", 10) == 0) { LOGGER_set_output_mode (_LOGGER_STDERR); } else if (strncmp (&(argv[i][2]), "stdout", 9) == 0) { LOGGER_set_output_mode (_LOGGER_STDOUT); } else if (strncmp (&(argv[i][2]), "no_nameless", 11) == 0) { MIME_set_no_nameless (1); } else if (strncmp (&(argv[i][2]), "no-nameless", 11) == 0) { MIME_set_no_nameless (1); } else if (strncmp (&(argv[i][2]), "debug", 5) == 0) { MIME_set_debug (1); } else if (strncmp (&(argv[i][2]), "mailbox", 7) == 0) { MIME_set_mailboxformat (1); } else if (strncmp(&(argv[i][2]), "formdata", 8) == 0) { // Form data usually contains embedded \0 sequences // so we need to explicitly turn this off. FFGET_set_allow_nul(1); } else if (strncmp (&(argv[i][2]), "no_uudecode", 11) == 0) { // We are transitioning away from negative-logic function // names because they can cause confusion when reading, so // from here on, we will use things like _set_foo() rather // than _set_no_foo() MIME_set_decode_uudecode(0); } else if (strncmp (&(argv[i][2]), "no-uudecode", 11) == 0) { MIME_set_decode_uudecode(0); } else if (strncmp (&(argv[i][2]), "no-tnef", 7) == 0) { MIME_set_decode_tnef (0); } else if (strncmp (&(argv[i][2]), "no-ole", 6) == 0) { MIME_set_decode_ole(0); } else if (strncmp (&(argv[i][2]), "no-base64", 9) == 0) { MIME_set_decode_base64(0); } else if (strncmp (&(argv[i][2]), "no-quotedprintable", strlen("no-quotedprintable")) == 0) { MIME_set_decode_qp(0); } else if (strncmp(&(argv[i][2]), "no-doublecr", strlen("no-doublecr")) == 0) { MIME_set_decode_doubleCR(0); } else if (strncmp(&(argv[i][2]), "no-mht", strlen("no-mht")) == 0) { MIME_set_decode_mht(0); } else if (strncmp(&(argv[i][2]), "qmail-bounce", strlen("qmail-bounce")) == 0) { MIME_set_header_longsearch(1); } else if (strncmp(&(argv[i][2]), "disable-qmail-bounce", strlen("disable-qmail-bounce")) == 0) { MIME_set_header_longsearch(0); } else if (strncmp(&(argv[i][2]), "no-multiple-filenames", strlen("no-multiple-filenames")) == 0) { MIME_set_multiple_filenames(0); } else if (strncmp(&(argv[i][2]), "recursion-max", strlen("recursion-max")) == 0) { if (argv[i+1] != NULL) { int level; level = atoi(argv[i+1]); if (level > 0) { MIME_set_recursion_level(level); } } } else if (strncmp(&(argv[i][2]), "timeout", strlen("timeout")) == 0) { if (argv[i+1] != NULL) { int seconds; seconds = atoi(argv[i+1]); if (seconds > 0) { glb->timeout = seconds; } } } else if (strncmp (&(argv[i][2]), "buildcodes", 10) == 0) { fprintf(stdout,"%s\n%s\n%s\n", BUILD_CODE, BUILD_DATE, BUILD_BOX); exit(0); } else if (strncmp (&(argv[i][2]), "version", 7) == 0) { fprintf (stdout, "%s\n", version); exit (0); } else if (strncmp(&(argv[i][2]), "extended-errors", strlen("extended-errors")) == 0) { glb->use_return_codes = 1; } else { LOGGER_log ("Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } break; // else, just dump out the help message default: LOGGER_log ("Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } // Switch argv[i][1] } // if argv[i][0] == - } // for return result; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_init Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_init (struct RIPMIME_globals *glb) { glb->dir = defaultdir; glb->inputfile = NULL; glb->use_return_codes = 0; glb->timeout = 0; glb->quiet = 0; glb->verbose_defects = 0; glb->verbose = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_signal_alarm Returns Type : void ----Parameter List 1. int sig , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ void RIPMIME_signal_alarm( int sig ) { if (ripmime_globals->quiet == 0) LOGGER_log("%s:%d:RIPMIME_signal_alarm: ripMIME took too long to complete. Mailpack is \"%s\", output dir is \"%s\"",FL, ripmime_globals->inputfile, ripmime_globals->dir ); exit(RIPMIME_ERROR_TIMEOUT); } /*-----------------------------------------------------------------\ Function Name : RIPMIME_unpack_single Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, 2. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_unpack_single( struct RIPMIME_globals *glb, char *fname ) { int result = 0; /** Set the alarm timeout feature. **/ if (glb->timeout > 0) { signal(SIGALRM, RIPMIME_signal_alarm); alarm(glb->timeout); } MIMEH_set_outputdir (glb->dir); result = MIME_unpack (glb->dir, fname, 0); // do any last minute things MIME_close (); return result; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_unpack Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_unpack( struct RIPMIME_globals *glb ) { struct stat st; int stat_result = 0; int result = 0; int input_is_directory = 0; /** If we're not inputting from STDIN, check to see if the ** input is a directory **/ if (strcmp(glb->inputfile,"-")!=0) { stat_result = stat(glb->inputfile, &st); if (stat_result != 0) return -1; if (S_ISDIR(st.st_mode)) input_is_directory = 1; } if (input_is_directory == 1) { /** Unpack all files in directory **/ DIR *dir; struct dirent *dir_entry; fprintf(stderr,"input file is a directory, recursing\n"); dir = opendir(glb->inputfile); if (dir == NULL) return -1; do { /** Check every entry in the directory provided and if it's a file ** try to unpack it **/ char fullfilename[1024]; dir_entry = readdir( dir ); if (dir_entry == NULL) break; if (strcmp(dir_entry->d_name, ".")==0) continue; if (strcmp(dir_entry->d_name, "..")==0) continue; snprintf(fullfilename,sizeof(fullfilename),"%s/%s", glb->inputfile, dir_entry->d_name); stat_result = stat( fullfilename, &st ); if (stat_result != 0) continue; if (S_ISREG(st.st_mode)) { /** If the directory entry is a file, then unpack it **/ fprintf(stdout,"Unpacking mailpack %s\n",fullfilename); result = RIPMIME_unpack_single(glb, fullfilename); } } while (dir_entry != NULL); /** While more files in the directory **/ closedir( dir ); } else { /** If the supplied file was actually a normal file, then decode normally **/ result = RIPMIME_unpack_single( glb, glb->inputfile ); } return result; } /*-----------------------------------------------------------------\ Function Name : main Returns Type : int ----Parameter List 1. int argc, 2. char **argv, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int main (int argc, char **argv) { struct RIPMIME_globals glb; int result = 0; /* if the user has just typed in "ripmime" and nothing else, then we had better give them * the rundown on how to use this program */ if (argc < 2) { fprintf (stderr, "%s\n%s", version, help); return RIPMIME_ERROR_INSUFFICIENT_PARAMETERS; } // Set the global pointer ripmime_globals to point to // the glb struct, so that if we have a timeout condition // we can use the data ripmime_globals = &glb; // Set up our initial logging mode - so that we can always get // report messages if need be. LOGGER_set_output_mode (_LOGGER_STDOUT); // Perform system initialisations MIME_init (); RIPMIME_init (&glb); // Setup our default behaviours */ MIME_set_uniquenames (1); MIME_set_paranoid (0); MIME_set_header_longsearch(1); // 20040310-0117:PLD - Added by default as it seems stable, use --disable-qmail-bounce to turn off MIME_set_renamemethod (_MIME_RENAME_METHOD_INFIX); RIPMIME_parse_parameters (&glb, argc, argv); // if our input filename wasn't specified, then we better let the user know! if (!glb.inputfile) { LOGGER_log("Error: No input file was specified\n"); return RIPMIME_ERROR_NO_INPUT_FILE; } // Fire up the randomizer srand (time (NULL)); // clean up the output directory name if required (remove any trailing /'s, as suggested by James Cownie 03/02/2001 if (glb.dir[strlen (glb.dir) - 1] == '/') { glb.dir[strlen (glb.dir) - 1] = '\0'; } // Create the output directory required as specified by the -d parameter if (glb.dir != defaultdir) { result = mkdir (glb.dir, S_IRWXU); // if we had a problem creating a directory, and it wasn't just // due to the directory already existing, then we have a bit of // a problem on our hands, hence, report it. // if ((result == -1) && (errno != EEXIST)) { LOGGER_log("ripMIME: Cannot create directory '%s' (%s)\n", glb.dir, strerror (errno)); return RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR; } } // Unpack the contents RIPMIME_unpack(&glb); // Possible exit codes include; // 0 - all okay // 240 - processing stopped due to recursion limit if (glb.use_return_codes == 0) result = 0; return result; } /*-END-----------------------------------------------------------*/ p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/0000755000175000001440000000000010347164447015153 5ustar jlaiusersp3scan-2.3.2/ripmime-1.4.0.6/ripOLE/TODO0000644000175000001440000000005710347164455015644 0ustar jlaiusers ----------------------- Make things work :-) p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/Makefile0000644000175000001440000000046610347164455016620 0ustar jlaiusers OBJS= ole.o olestream-unwrap.o bytedecoders.o logger.o pldstr.o bt-int.o CFLAGS=-Wall -g -O2 -I. .c.o: $(CC) $(CFLAGS) $(DEFINES) -c $*.c default: ripole clean: rm -f *.o ripole ripole: $(OBJS) ripole.[ch] $(CC) $(CFLAGS) $(OBJS) $(DEFINES) ripole.c -o ripole validate: ripole cp ripole validate p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/CONTRIBUTORS0000644000175000001440000000025410347164455017033 0ustar jlaiusers Original code by Paul L Daniels Source documentation supplied by: defrost, Chris Hine and google Patches: Chris Hine:0.0.3:2003-07-04: Buffer,bp initialisation patch. p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/LICENSE0000644000175000001440000000270410347164455016162 0ustar jlaiusersCopyright (c) 2003, PLD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the PLD nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/ole.c0000644000175000001440000015660510347164455016112 0ustar jlaiusers#include #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "bt-int.h" #include "bytedecoders.h" #include "olestream-unwrap.h" #include "ole.h" /** Sector ID values (predefined) **/ #define OLE_SECTORID_FREE -1 /** Unallocated sector **/ #define OLE_SECTORID_ENDOFCHAIN -2 /** Sector marks the end of the a sector-ID chain **/ #define OLE_SECTORID_SAT -3 /** Sector used by sector allocation Table **/ #define OLE_SECTORID_MSAT -4 /** Sector used by master sector allocation Table **/ // Main header accessors #define header_id(x) ((x) +0) #define header_clid(x) ((x) +0x08) #define header_minor_version(x) ((x) +0x18) #define header_dll_version(x) ((x) +0x1a) #define header_byte_order(x) ((x) +0x1c) #define header_sector_shift(x) ((x) +0x1e) #define header_mini_sector_shift(x) ((x) +0x20) #define header_fat_sector_count(x) ((x) +0x2c) #define header_directory_stream_start_sector(x) ((x) +0x30) #define header_mini_cutoff_size(x) ((x) +0x38) #define header_mini_fat_start(x) ((x) +0x3c) #define header_mini_fat_sector_count(x) ((x) +0x40) #define header_dif_start_sector(x) ((x) +0x44) #define header_dif_sector_count(x) ((x) +0x48) #define header_fat_sectors(x) ((x) +0x4c) //Property Storage accessor macros #define pps_rawname(x) ((x) +0) #define pps_sizeofname(x) ((x) +0x40) #define pps_type(x) ((x) +0x42) #define pps_previouspps(x) ((x) +0x44) #define pps_nextpps(x) ((x) +0x48) #define pps_directorypps(x) ((x) +0x4c) #define pps_time1seconds(x) ((x) +0x64) #define pps_time1days(x) ((x) +0x68) #define pps_time2seconds(x) ((x) +0x6c) #define pps_time2days(x) ((x) +0x70) #define pps_propertystart(x) ((x) +0x74) #define pps_sizeofproperty(x) ((x) +0x78) // Type lenghts #define LEN_BYTE 1 #define LEN_USHORT 2 #define LEN_ULONG 4 // Directory types #define STGTY_INVALID 0 #define STGTY_STORAGE 1 #define STGTY_STREAM 2 #define STGTY_LOCKBYTES 3 #define STGTY_PROPERTY 4 #define STGTY_ROOT 5 // Directory tag colours #define DE_RED 0 #define DE_BLACK 1 #define DOLE if (OLE_DNORMAL(ole->debug)) #define VOLE if (ole->verbose) unsigned char OLE_id_v2[]={ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; unsigned char OLE_id_v1[]={ 0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0 }; /*-----------------------------------------------------------------\ Function Name : OLE_version Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_version( void ) { fprintf(stderr,"ripOLE: %s\n", LIBOLE_VERSION); return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_init Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 20041127-2029:PLD: Added file_size initialization \------------------------------------------------------------------*/ int OLE_init( struct OLE_object *ole ) { ole->debug = 0; ole->verbose = 0; ole->quiet = 0; ole->filename_report_fn = NULL; ole->f = NULL; ole->file_size = 0; ole->FAT = NULL; ole->FAT_limit = NULL; ole->miniFAT = NULL; ole->miniFAT_limit = NULL; ole->header_block[0] = '\0'; ole->ministream = NULL; ole->properties = NULL; ole->header.sector_shift = 0; ole->header.mini_sector_shift = 0; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_dir_init Returns Type : int ----Parameter List 1. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_dir_init(struct OLE_directory_entry *dir ) { memset(dir->element_name,'\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE); dir->element_name_byte_count = 0; dir->element_type = 0; dir->element_colour = 0; dir->left_child = 0; dir->right_child = 0; dir->root = 0; dir->class[0] = '\0'; dir->userflags = 0; dir->timestamps[0] = '\0'; dir->start_sector = 0; dir->stream_size = 0; return 0; }; /*-----------------------------------------------------------------\ Function Name : OLE_set_verbose Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_verbose( struct OLE_object *ole, int level ) { ole->verbose = level; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_quiet Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_quiet( struct OLE_object *ole, int level ) { ole->quiet = level; ole->verbose = 0; ole->debug = 0; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_debug Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_debug( struct OLE_object *ole, int level ) { ole->debug = level; if (ole->debug > 0) LOGGER_log("%s:%d:OLE_set_debug: Debug level set to %d",FL, ole->debug); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_save_unknown_streams Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_save_unknown_streams( struct OLE_object *ole, int level ) { ole->save_unknown_streams = level; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_sectorpos Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int SID , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Given a sector ID, this function will return the file position offset. Assumes that the offset for the file starts at 512 bytes (which is the size of the OLE header) -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_sectorpos( struct OLE_object *ole, int SID ) { int pos = 0; pos = 512 +(SID *ole->header.sector_size); return pos; } /*-----------------------------------------------------------------\ Function Name : OLE_get_block Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int block_index, Block indexes / Sector ID's are signed ints. 3. unsigned char *block_buffer , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_block( struct OLE_object *ole, int block_index, unsigned char *block_buffer ) { if (block_buffer == NULL) { LOGGER_log("%s:%d:OLE_get_block:ERROR: Block buffer is NULL",FL); return -1; } if (ole->f != NULL) { int read_count = 0; int fseek_result = 0; size_t offset = 0; unsigned char *bb = NULL; bb = malloc(sizeof(unsigned char) *ole->header.sector_size); if (bb == NULL) { LOGGER_log("%s:%d:OLE_get_block:ERROR: Cannot allocate %d bytes for OLE block",FL, ole->header.sector_size); return -1; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: BlockIndex=%d, Buffer=0x%x",FL, block_index, block_buffer); //20051211-2343:PLD: offset = (block_index +1) << ole->header.sector_shift; offset = OLE_sectorpos(ole, block_index); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Read offset in file = 0x%x size to read= 0x%x",FL,offset,ole->header.sector_size); fseek_result = fseek(ole->f, offset, SEEK_SET); if (fseek_result != 0) { if (bb != NULL) { free(bb); bb = NULL; } LOGGER_log("%s:%d:OLE_get_block:ERROR: Seek failure (block=%d:%d)",FL, block_index,offset, strerror(errno)); return OLEER_GET_BLOCK_SEEK; } //read_count = fread(block_buffer, sizeof(unsigned char), ole->header.sector_size, ole->f); read_count = fread(bb, sizeof(unsigned char), ole->header.sector_size, ole->f); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Read %d byte of data",FL,read_count); if (read_count != (int)ole->header.sector_size) { if (bb != NULL){ free(bb); bb = NULL; } VOLE LOGGER_log("%s:%d:Mismatch in bytes read. Requested %d, got %d\n", FL, ole->header.sector_size, read_count); return OLEER_GET_BLOCK_READ; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Copying over memory read from file",FL); memcpy(block_buffer, bb, ole->header.sector_size); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: memory block copied to block_buffer",FL); /* We're now done with BB, dispose of it */ if (bb) { free(bb); bb = NULL; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Disposed of temporary bb block",FL); } else { LOGGER_log("%s:%d:OLE_get_block:ERROR: OLE file is closed\n",FL); return -1; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Done",FL); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_get_miniblock Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. unsigned int block_index, 3. unsigned char *block_buffer , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_miniblock( struct OLE_object *ole, int block_index, unsigned char *block_buffer ) { if (ole->ministream) { int offset = block_index << ole->header.mini_sector_shift; memcpy( block_buffer, ole->ministream +offset, ole->header.mini_sector_size); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_dbstosbs Returns Type : int ----Parameter List 1. char *raw_string, 2. size_t char_count, 3. char *clean_string , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_dbstosbs( char *raw_string, size_t byte_count, char *clean_string, int clean_string_len ) { char *limit = raw_string +byte_count -1; clean_string_len--; while ((raw_string < limit)&&(byte_count >0)&&(byte_count--)&&(clean_string_len--)) { int v = (char)*raw_string; if (isprint(v)) { *clean_string = v; clean_string++; } raw_string += 2; } *clean_string = '\0'; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_string Returns Type : int ----Parameter List 1. char *string, 2. size_t length , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_string( char *string, size_t char_count) { while (char_count--) { printf("%c",*string); string += 2; } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_sector Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. unsigned char *sector , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_sector( struct OLE_object *ole, char *sector, unsigned int bytes) { int current_byte; int ubytes = bytes; printf("\n"); for (current_byte = 0; current_byte < ubytes; current_byte++ ) { printf("%02X ", *(sector +current_byte)); if (((current_byte+1) %32)==0) { int j; for (j = current_byte -31; j <=current_byte; j++) { if (isalnum(*(sector +j))) printf("%c",*(sector+j)); else printf("."); } printf("\n"); } } printf("\n"); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_is_OLE_file Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_is_file_OLE( struct OLE_object *ole ) { if (memcmp(OLE_id_v1, ole->header_block, sizeof(OLE_id_v1))==0) return 1; if (memcmp(OLE_id_v2, ole->header_block, sizeof(OLE_id_v2))==0) return 1; return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_get_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_header( struct OLE_object *ole ) { int result = 0; ole->header.sector_size = OLE_HEADER_BLOCK_SIZE; result = OLE_get_block( ole, -1, ole->header_block ); if (OLE_is_file_OLE( ole ) == 0) { return OLEER_NOT_OLE_FILE; } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_convert_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_convert_header( struct OLE_object *ole ) { struct OLE_header *h; unsigned char *hb; /** pointer to the header block **/ unsigned char *fat_start; unsigned int i; h = &(ole->header); hb = ole->header_block; /** Note that the header_*(hb) calls are actually macros which are ** defined in the ole.h file. These macros basically take the ** hb value and add the required offset, they don't affect the ** value of hb (or they certainly SHOULD NOT! ) **/ h->minor_version = get_2byte_value(header_minor_version(hb)); h->dll_version = get_2byte_value(header_dll_version(hb)); h->byte_order = get_2byte_value(header_byte_order(hb)); /** 0xFEFF = Little endian, 0xFFFE = big endian **/ h->sector_shift = get_2byte_value(header_sector_shift(hb)); h->sector_size = 1 << h->sector_shift; h->mini_sector_shift = get_2byte_value(header_mini_sector_shift(hb)); h->mini_sector_size = 1 << h->mini_sector_shift; h->fat_sector_count = get_4byte_value(header_fat_sector_count(hb)); /** Total number of sectors use for the SAT **/ h->directory_stream_start_sector = get_4byte_value(header_directory_stream_start_sector(hb)); /** Start sector-ID for the DIRECTORY STREAM **/ h->mini_cutoff_size = get_4byte_value(header_mini_cutoff_size(hb)); h->mini_fat_start = get_4byte_value(header_mini_fat_start(hb)); h->mini_fat_sector_count = get_4byte_value(header_mini_fat_sector_count(hb)); h->dif_start_sector = get_4byte_value(header_dif_start_sector(hb)); h->dif_sector_count = get_4byte_value(header_dif_sector_count(hb)); /** Compute the maximum possible sector number by taking our OLE filesize ** and dividing it by the size of our sector size. While this isn't ** absolutely accurate it is at least useful in providing us with an ** upper-bound of what is an acceptable sector ID **/ ole->last_sector = ole->file_size >> h->sector_shift; /** Decode our first 109 sector-ID's into the master sector allocation table (MSAT/FAT) **/ fat_start = header_fat_sectors(hb); for (i = 0; i < h->fat_sector_count; i++) { if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break; h->FAT[i] = get_4byte_value( fat_start +(LEN_ULONG *i)); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_header_sanity_check Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Determines the degree of insanity in the header, returning it as an integer, 1 per degree of insanity. -------------------------------------------------------------------- Changes: 20041127-2027:PLD: Initial version \------------------------------------------------------------------*/ int OLE_header_sanity_check( struct OLE_object *ole ) { int insanity=0; int max_sectors; struct OLE_header *h; h = &(ole->header); max_sectors = ole->file_size / h->sector_size; if (h->sector_shift > 20) insanity++; if (h->mini_sector_shift > 10) insanity++; if (h->fat_sector_count < 0) insanity++; if (h->fat_sector_count > max_sectors) insanity++; if (h->directory_stream_start_sector > max_sectors) insanity++; return insanity; } /*-----------------------------------------------------------------\ Function Name : OLE_print_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_header( struct OLE_object *ole ) { unsigned int i; struct OLE_header *h; h = &(ole->header); printf( "Minor version = %d\n" "DLL version = %d\n" "Byte order = %d\n\n" "Sector shift = %d\n" "Sector size = %d\n" "Mini Sector shift = %d\n" "Mini sector size = %d\n\n" "FAT sector count = %d\n" "First FAT sector = %d\n\n" "Maximum ministream size = %d\n\n" "First MiniFAT sector = %d\n" "MiniFAT sector count = %d\n\n" "First DIF sector = %d\n" "DIF sector count = %d\n" "--------------------------------\n" ,h->minor_version ,h->dll_version ,h->byte_order ,h->sector_shift ,h->sector_size ,h->mini_sector_shift ,h->mini_sector_size ,h->fat_sector_count ,h->directory_stream_start_sector ,h->mini_cutoff_size ,h->mini_fat_start ,h->mini_fat_sector_count ,h->dif_start_sector ,h->dif_sector_count ); // Print out the FAT chain for (i = 0; i < h->fat_sector_count; i++) { if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break; // We can't read beyond the 109th sector location printf("FAT[%d] = %d\n", i, h->FAT[i]); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_convert_directory Returns Type : int ----Parameter List 1. unsigned char *buf, 2. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_convert_directory( struct OLE_object *ole, unsigned char *buf, struct OLE_directory_entry *dir ) { /** Converts a raw block of 128 bytes from the file to a ** struct OLE_directory_entry data structure **/ /** Flush the element name **/ memset( dir->element_name, '\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE); /** The first 64 bytes of the structure are the element's name ** in 16-bite UNICODE, meaning a maximum of 31 characters when ** we account for the trailing zero byte **/ /** Copy the first 64 bytes of our *buf parameter into the element name **/ memcpy( dir->element_name, buf, OLE_DIRECTORY_ELEMENT_NAME_SIZE ); /** how many bytes of the above 64 bytes are used for the name (NOT CHARACTERS!), ** ** example, for a 8 character string with a trailing zero we use ** ** (8+1)*2 = 18 bytes **/ dir->element_name_byte_count = get_2byte_value( buf + 0x40 ); /** Element type is of the following: ** 0x00 - empty ** 0x01 - user storage ** 0x02 - user stream ** 0x03 - lock bytes (we don't know what this is for) ** 0x04 - property (again, we don't know) ** 0x05 - root storage **/ dir->element_type = get_1byte_value( buf +0x42 ); /** Element colour for the red-black tree: ** 0x00 - Red ** 0x01 - Black **/ dir->element_colour = get_1byte_value( buf +0x43 ); /** Directory ID (DID) of the left child, -1 if no sibling **/ dir->left_child = get_4byte_value( buf +0x44 ); /** Directory ID (DID) of the right child, -1 if no sibling **/ dir->right_child = get_4byte_value( buf +0x48 ); /** Directory ID (DID) of the root node entry of the RB tree of all ** storage members (if this entry is a storage), else -1. **/ dir->root = get_4byte_value( buf +0x4c ); memcpy( dir->class, buf +0x50, 16 ); dir->userflags = get_4byte_value( buf +0x60 ); memcpy( dir->timestamps, buf +0x64, 16 ); /** Actually consists of 2 8 byte stamps **/ /** Sector ID of the first sector or short-sector **/ dir->start_sector = get_4byte_value( buf +0x74 ); /** Size of this stream **/ DOLE LOGGER_log("%s:%d:OLE_directory_entry:DEBUG: stream size = 0x%x %x %x %x" ,FL ,*(buf +0x78) ,*(buf +0x79) ,*(buf +0x7A) ,*(buf +0x7B) ); dir->stream_size = get_4byte_value( buf +0x78 ); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_directory Returns Type : int ----Parameter List 1. struct OLE *ole, 2. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_directory( struct OLE_object *ole, struct OLE_directory_entry *dir ) { char element[64]; OLE_dbstosbs( dir->element_name, dir->element_name_byte_count, element, sizeof(element) ); printf( "Element Name = %s\n" "Element type = %d\n" "Element colour = %d\n" "Left Child = %d\n" "Right Child = %d\n" "Root = %d\n" "User flags = %d\n" "Start sector = %d\n" "Stream Size = %d\n" ,element ,dir->element_type ,dir->element_colour ,dir->left_child ,dir->right_child ,dir->root ,dir->userflags ,dir->start_sector ,dir->stream_size ); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_load_FAT Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_load_FAT( struct OLE_object *ole ) { unsigned int FAT_size; FAT_size = ole->header.fat_sector_count << ole->header.sector_shift; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG:Allocating for %d sectors (%d bytes) \n" ,FL,ole->header.fat_sector_count, FAT_size); ole->FAT = malloc( FAT_size *sizeof(unsigned char)); ole->FAT_limit = ole->FAT +FAT_size; if (ole->FAT != NULL) { unsigned int i; unsigned char *fat_position = ole->FAT; unsigned int sector_count = ole->header.fat_sector_count; if (sector_count > OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) { sector_count = OLE_HEADER_FAT_SECTOR_COUNT_LIMIT; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: sector count greater than limit; set to %d",FL, sector_count); } // Load in all our primary-FAT sectors from the OLE file for (i = 0; i < sector_count; i++) { int getblock_result = 0; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Loading sector %d",FL, i); getblock_result = OLE_get_block(ole, ole->header.FAT[i], fat_position); if (getblock_result != 0) { // if the get block fails, return the error - but keep the FAT // pointer alive - so that we can facilitate debugging // otherwise the caller is always going to get a NULL pointer // and have no idea to what extent the data was read. // // This behavior may be changed later - but for now (beta development) // it'll be okay to leave it here - just make sure we know to // free the FAT block later. return getblock_result; } fat_position += ole->header.sector_size; if (fat_position > ole->FAT_limit) { LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: FAT boundary limit exceeded %p > %p", FL, fat_position, ole->FAT_limit); return -1; } } // If our DIF count is > 0, this means we have a pretty big // file on hand (> 7Mb) and thus we now have to do some // fancy double-dereferenced sector request - enough to // twist your brain if you're not alert, you have been // warned. if (ole->header.dif_sector_count > 0) { unsigned char *fat_block; unsigned char *fat_block_end; unsigned int current_sector = ole->header.dif_start_sector; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Allocating %d bytes to fat_block\n",FL, ole->header.sector_size); fat_block = malloc( ole->header.sector_size ); if (fat_block == NULL) { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: Unable to allocate %d bytes\n", FL, ole->header.sector_size); return -1; // exit(1); } // We need to know where the end of this block is - because it's // used to show us where the NEXT FAT block is going to come from // NOTE - this only occurs if we do have another block, else // we'll simply have to just realise that we don't need any more // blocks and stop with this one. fat_block_end = fat_block +ole->header.sector_size -LEN_ULONG; // We know we've got 'dif_sector_count' blocks to read, each of // these blocks hold no more than 127 sector addresses which // contain the actual FAT data we're after (this is the double // dereference bit that twists your brain ) DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Loading DIF sectors (count = %d)",FL,ole->header.dif_sector_count); for (i = 0; i < ole->header.dif_sector_count; i++) { int import_sector; unsigned char *DIF = fat_block; int tick = 0; int getblock_result; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT index-data[%d] from sector 0x%x",FL,i,current_sector); getblock_result = OLE_get_block(ole, current_sector, fat_block); if (getblock_result != OLE_OK) { if (fat_block) free(fat_block); return getblock_result; } if (OLE_DPEDANTIC(ole->debug)) OLE_print_sector(ole, fat_block, ole->header.sector_size); // Now, traverse this block until we hit a < 0 // If we get what is a non-valid sector value // we know we've reached the end of the valid // sectors from which to read more extended FAT // data. do { import_sector = get_4byte_value( DIF ); DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: import sector = 0x%x",FL,import_sector); if (import_sector >= 0) { if (fat_position +ole->header.sector_size <= ole->FAT_limit) { DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT-data[%d] from sector 0x%x",FL,tick,import_sector); getblock_result = OLE_get_block(ole, import_sector, fat_position); if (getblock_result != OLE_OK) { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: Not able to load block, import sector = 0x%x, fat position = 0x%x",FL, import_sector, fat_position); if (fat_block) free(fat_block); return getblock_result; } fat_position += ole->header.sector_size; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: FAT position = 0x%x (start = 0x%x, end = 0x%x)" ,FL ,fat_position ,fat_block ,ole->FAT_limit ); //if (fat_position +ole->header.sector_size > ole->FAT_limit) if (fat_position > ole->FAT_limit) { DOLE LOGGER_log("%s:%d:OLE_load_FAT:ERROR: FAT memory boundary limit exceeded %p >= %p",FL,fat_position,ole->FAT_limit); if (fat_block) free(fat_block); return OLEER_MEMORY_OVERFLOW; } tick++; DIF += LEN_ULONG; } else { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: FAT memory boundary limit exceeded %p >= %p",FL,fat_position,ole->FAT_limit); if (fat_block) free(fat_block); return OLEER_MEMORY_OVERFLOW; } } else { VOLE LOGGER_log("%s:%d:OLE_load_FAT:ERROR: sector request was negative (%d)",FL, import_sector); } DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: DIF = 0x%x",FL,DIF); } while ((import_sector >= 0)&&(DIF < fat_block_end)); // Get the next sector of DIF/XBAT data ... // // If we still have more sectors full of extended FAT // sectors that we have to read, then we neet to // obtain the address of the next FAT-sector filled // sector if ( i < ole->header.dif_sector_count -1 ) { current_sector = get_4byte_value( fat_block_end ); DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Next DIF/XBAT index sector located at 0x%x",FL,current_sector); if (current_sector < 0) break; } } // For every DIF/XBAT sector we're supposed to read if (fat_block) free(fat_block); } // If we have DIF/XBAT sectors to read into the FAT } // If we managed to allocate memory for our FAT table return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_follow_chain Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_follow_chain( struct OLE_object *ole, int FAT_sector_start ) { int current_sector = FAT_sector_start; int chain_length=0; int last_sector_of_file = ole->last_sector; int break_out = 0; struct bti_node *n; BTI_init(&n); if (FAT_sector_start < 0) return 0; DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: Starting chain follow at sector %d",FL, FAT_sector_start ); do { int next_sector; unsigned char *next_sector_location; next_sector_location = ole->FAT +(LEN_ULONG *current_sector); if (next_sector_location > (ole->FAT_limit -4)) { DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: ERROR: Next sector was outside of the limits of this file (%ld > %ld)",FL, next_sector_location, ole->FAT_limit); break; } //next_sector = get_4byte_value( ole->FAT +(LEN_ULONG *current_sector)); next_sector = get_4byte_value( next_sector_location ); if (BTI_add(&n, next_sector) != 0) { DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: Sector collision, terminating chain traversal",FL); chain_length=-1; break; } DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: 0x%0X:%d)->(0x%0X:%d)\n",FL, current_sector, current_sector, next_sector, next_sector); // 20040729-10H37 Added this to prevent endless loop which sometimes occurs at sector 0 if (next_sector == current_sector) break; // fflush(stdout); current_sector = next_sector; chain_length++; /** Test to see if we should terminate this chain traversal **/ switch (current_sector) { case OLE_SECTORID_MSAT: case OLE_SECTORID_SAT: case OLE_SECTORID_ENDOFCHAIN: case OLE_SECTORID_FREE: break_out=1; break; default: break_out=0; }; if (current_sector < 0) break_out = 1; } while ((break_out==0)&&(current_sector < last_sector_of_file)); BTI_done(&n); return chain_length; } /*-----------------------------------------------------------------\ Function Name : OLE_follow_minichain Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_follow_minichain( struct OLE_object *ole, int miniFAT_sector_start ) { //unsigned int current_sector = miniFAT_sector_start; int current_sector = miniFAT_sector_start; int chain_length=0; int break_out = 0; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Starting at sector %d",FL, miniFAT_sector_start); if (miniFAT_sector_start < 0) return 0; do { //unsigned int next_sector; int next_sector; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Requesting 4-byte value at '%d'",FL, ole->miniFAT +(LEN_ULONG *current_sector)); if (ole->miniFAT +(LEN_ULONG *current_sector) > ole->miniFAT_limit) { DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Requested location is out of bounds\n",FL); return 0; } next_sector = get_4byte_value( ole->miniFAT +(LEN_ULONG *current_sector)); DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Current Msector(0x%0X:%d)->next(0x%0X:%d)\n", FL, current_sector, current_sector, next_sector, next_sector); /** Check for conditions that indicate we should stop traversing this chain **/ /** 1. We cannot point to ourselves **/ if (current_sector == next_sector) break; chain_length++; current_sector = next_sector; /** Test for non-positive type sector ID's **/ switch (current_sector) { case OLE_SECTORID_MSAT: case OLE_SECTORID_SAT: case OLE_SECTORID_ENDOFCHAIN: case OLE_SECTORID_FREE: break_out=1; break; default: break_out=0; }; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: current sector = %d",FL,current_sector); } while ((break_out==0)&&(current_sector <= ole->last_sector )); DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Done. Chainlength=%d",FL, chain_length); return chain_length; } /*-----------------------------------------------------------------\ Function Name : char Returns Type : unsigned ----Parameter List 1. *OLE_load_minichain( struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: PLD:2003-Aug-28: Added sanity checking on the miniFAT_sector_start value so that we didn't try to load up a miniFAT starting on a negative value \------------------------------------------------------------------*/ unsigned char *OLE_load_minichain( struct OLE_object *ole, int miniFAT_sector_start ) { int chain_length = 0; int current_sector = miniFAT_sector_start; unsigned char *buffer; unsigned char *bp; DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Loading minichain starting at %d",FL, miniFAT_sector_start); // Added this sanity checking 2003 Aug 28 if (miniFAT_sector_start < 0) return NULL; chain_length = OLE_follow_minichain( ole, miniFAT_sector_start ); DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Found %d mini-sectors to load (%d bytes)\n",FL, chain_length, chain_length *ole->header.mini_sector_size); // 20040911-21H59 // If our chain is 0 length, then there's nothing to return if (chain_length == 0) return NULL; bp = buffer = malloc( chain_length *ole->header.mini_sector_size *sizeof(unsigned char)); if (buffer != NULL) { do { int next_sector; DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Loading sector %d",FL, current_sector); OLE_get_miniblock( ole, current_sector, bp ); bp += ole->header.mini_sector_size; next_sector = get_4byte_value( ole->miniFAT +(LEN_ULONG *current_sector)); current_sector = next_sector; } while ((current_sector != OLE_SECTORID_ENDOFCHAIN)&&(current_sector >= 0)&&(current_sector <= ole->last_sector)); } else { LOGGER_log("%s:%d:OLE_get_miniblock:ERROR: Failed to allocate enough memory for miniChain",FL); } DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Done. buffer=%p",FL, buffer); return buffer; } /*-----------------------------------------------------------------\ Function Name : char Returns Type : unsigned ----Parameter List 1. *OLE_load_chain( struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: Make the loading aware of negative-value sectors so that it can make more intelligent exit strategies. \------------------------------------------------------------------*/ unsigned char *OLE_load_chain( struct OLE_object *ole, int FAT_sector_start ) { int chain_length = 0; int current_sector = FAT_sector_start; unsigned char *buffer = NULL; unsigned char *bp = NULL; ole->last_chain_size = 0; if (FAT_sector_start < 0) return NULL; DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading chain, starting at sector %d",FL,FAT_sector_start); chain_length = OLE_follow_chain( ole, FAT_sector_start ); DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: %d sectors need to be loaded",FL,chain_length); if (chain_length > 0) { size_t offset; offset = ole->last_chain_size = chain_length << ole->header.sector_shift; bp = buffer = malloc( offset *sizeof(unsigned char)); if (buffer == NULL) { LOGGER_log("%s:%d:OLE_load_chain:ERROR: Cannot allocate %d bytes for OLE chain",FL,offset); return NULL; } if (buffer != NULL) { int tick = 0; unsigned char *bp_limit; bp_limit = bp +offset; do { int next_sector; DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading sector[%d] %d",FL, tick, current_sector ); ole->error = OLE_get_block( ole, current_sector, bp ); if (ole->error != OLE_OK) { //FREE5 if (bp != NULL) free(bp); return NULL; } bp += ole->header.sector_size; if (bp > bp_limit) { if (buffer != NULL) { free(buffer); bp = buffer = NULL; } VOLE LOGGER_log("%s:%d:OLE_load_chain:ERROR: Load-chain went over memory boundary",FL); return NULL; }; next_sector = get_4byte_value( ole->FAT +(LEN_ULONG *current_sector)); current_sector = next_sector; tick++; } while ((current_sector >= 0)&&(current_sector <= ole->last_sector)); } } DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Done loading chain",FL); return buffer; } /*-----------------------------------------------------------------\ Function Name : OLE_open_file Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *fullpath , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 20041127-2033:PLD: Added ole->file_size setting so that we can use this in the sanity checking to see if the requested sectors are outside of the possible valid filesize range. \------------------------------------------------------------------*/ int OLE_open_file( struct OLE_object *ole, char *fullpath ) { struct stat st; int stat_result; FILE *f; stat_result = stat(fullpath, &st); if (stat_result != 0) { DOLE LOGGER_log("%s:%d:OLE_open_file:ERROR: Cannot locate file '%s' for opening (%s)",FL, fullpath, strerror(errno)); return OLEER_BAD_INPUT_FILE; } DOLE LOGGER_log("%s:%d:OLE_open_file:DEBUG: File size of %s = %ld",FL, fullpath, st.st_size); if ((stat_result == 0) && (st.st_size < 512)) return OLEER_NOT_OLE_FILE; ole->file_size = st.st_size; f = fopen(fullpath,"r"); if (f == NULL) { ole->f = NULL; if (ole->quiet == 0) { LOGGER_log("%s:%d:OLE_open_file:ERROR:Cannot open %s for reading (%s)\n",FL,fullpath, strerror(errno)); } return -1; } else { ole->f = f; ole->file_size = st.st_size; ole->last_sector = -1; } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_open_directory Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *directory , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_open_directory( struct OLE_object *ole, char *directory ) { int result=0; result = mkdir( directory, S_IRWXU ); if ((result != 0)&&(errno != EEXIST)) { LOGGER_log("%s:%d:OLE_open_directory:ERROR: %s",FL,strerror(errno)); } else result = OLE_OK; return result; } /*-----------------------------------------------------------------\ Function Name : OLE_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This is merely a passthrough function to the OLEUW one, we do this in order to avoid having to force the calling parent from having to #include the OLEUW headers as well -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_filename_report_fn( struct OLE_object *ole, int (*ptr_to_fn)(char *) ) { ole->filename_report_fn = ptr_to_fn; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_store_stream Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *stream_name, 3. char *directory, 4. unsigned char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_store_stream( struct OLE_object *ole, char *stream_name, char *directory, char *stream, size_t stream_size ) { char *full_path = NULL; full_path = PLD_dprintf("%s/%s", directory, stream_name); if (full_path == NULL) { LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot compose full filename string from '%s' and '%s'", FL, directory, stream_name); return -1; } else { FILE *f; f = fopen(full_path,"w"); if (f == NULL) { LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot open %s for writing (%s)",FL, full_path, strerror(errno)); if (full_path) free(full_path); return -1; } else { size_t written_bytes; written_bytes = fwrite( stream, 1, stream_size, f ); if (written_bytes != stream_size) { LOGGER_log("%s:%d:OLE_store_stream:WARNING: Only wrote %d of %d bytes to file %s",FL,written_bytes,stream_size,full_path); } fclose(f); if ((OLE_VNORMAL(ole->verbose))&&(ole->filename_report_fn != NULL)) { ole->filename_report_fn( stream_name ); } } // if file is valid } // if full_path is valid if (full_path) free(full_path); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_decode_file_done Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_decode_file_done( struct OLE_object *ole ) { if (ole->f) fclose(ole->f); /** Why weren't these active? (they were commented out ) **/ if (ole->FAT) free(ole->FAT); if (ole->miniFAT) free(ole->miniFAT); if (ole->ministream) free(ole->ministream); if (ole->properties) free(ole->properties); return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_terminate_and_return Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int result , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_terminate_and_return( struct OLE_object *ole, int result ) { OLE_decode_file_done(ole); return result; } #ifdef RIPOLE_WALK_TREE int OLE_walk_tree( struct OLE_object *ole, char *fname, char *decode_path, int depth ) { /** Sanity check **/ if (depth > 100) return 0; if (ole->total_file_count > 10000) return 0; if (element_type < 0) return 0; switch (element_type) { case STGTY_ROOT: /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ DOLE LOGGER_log("%s:%d:OLE_walk_tree:DEBUG: Loading ministream/SmallBlockArray",FL); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: ministream done",FL); } } else if (adir->element_type == STGTY_STORAGE) { /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Item is directory, start child is at index %d\n",FL,i); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: DIRECTORY ministream done",FL); } #endif int OLE_decode_stream( struct OLE_object *ole, struct OLE_directory_entry *adir, char *decode_path ) { char *stream_data; struct OLEUNWRAP_object oleuw; int decode_result = OLEUW_STREAM_NOT_DECODED; char element_name[64]; int result = 0; memset(element_name, '\0', 64); OLE_dbstosbs( adir->element_name, adir->element_name_byte_count, element_name, 64 ); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Decoding stream '%s'",FL, element_name); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Initializing stream unwrapper",FL); OLEUNWRAP_init(&oleuw); OLEUNWRAP_set_debug(&oleuw,ole->debug); OLEUNWRAP_set_verbose(&oleuw,ole->verbose); OLEUNWRAP_set_filename_report_fn(&oleuw, ole->filename_report_fn); OLEUNWRAP_set_save_unknown_streams(&oleuw, ole->save_unknown_streams); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Unwrap engine set.",FL); if (adir->stream_size >= ole->header.mini_cutoff_size) { /** Standard size sector stored stream **/ /** Standard size sector stored stream **/ /** Standard size sector stored stream **/ DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Loading normal sized chain starting at sector %d",FL, adir->start_sector); stream_data = OLE_load_chain( ole, adir->start_sector ); if (stream_data == NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Terminating from stream data being NULL ",FL); //OLE_decode_file_done(ole); return OLEER_MINISTREAM_STREAM_READ_FAIL; } DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Normal decode START. element name ='%s' stream size = '%ld'",FL, element_name, adir->stream_size); decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, stream_data, adir->stream_size, decode_path ); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Normal decode done.",FL); } else { /** Minichain/Minisector stored stream **/ /** Minichain/Minisector stored stream **/ /** Minichain/Minisector stored stream **/ DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Minichain loader, starting at sector %d" ,FL ,adir->start_sector ); stream_data = OLE_load_minichain( ole, adir->start_sector ); if (stream_data == NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Ministream was non-existant, terminating",FL); //OLE_decode_file_done(ole); return OLEER_NORMALSTREAM_STREAM_READ_FAIL; } DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode START.",FL); decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, stream_data, adir->stream_size, decode_path ); DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode done.",FL); } if ((stream_data != NULL)&&(decode_result == OLEUW_STREAM_NOT_DECODED)&&(ole->save_unknown_streams)) { char *lfname; lfname = PLD_dprintf("ole-stream.%d",adir->start_sector); if (lfname != NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Saving stream to %s",FL,lfname); OLE_store_stream( ole, lfname, decode_path, stream_data, adir->stream_size ); free(lfname); } } // If we needed to save an unknown stream // Clean up an stream_data which we may have // read in from the chain-loader. if (stream_data) free(stream_data); return result; } /*-----------------------------------------------------------------\ Function Name : OLE_decode_file Returns Type : int ----Parameter List 1. char *fname, 2. char *decode_path , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_decode_file( struct OLE_object *ole, char *fname, char *decode_path ) { unsigned char *current_property, *property_limit; int result = 0; int i; // Reject any bad paramters. if (ole == NULL) return OLEER_DECODE_NULL_OBJECT; if (fname == NULL) return OLEER_DECODE_NULL_FILENAME; if (decode_path == NULL) return OLEER_DECODE_NULL_PATH; // We need to gain access to the OLE2 data file, without // this pretty much everything is pointless. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening %s", FL, fname ); result = OLE_open_file( ole, fname ); if (result != 0) return result; // Try create the output directory which we're using // to write the decoded files out to. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening output directory %s", FL, decode_path); result = OLE_open_directory( ole, decode_path ); if (result != 0) return result; // In order to successfully decode an OLE2 stream, we have to read // and understand the first 512 bytes of the file, this is the // OLE2 header. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Getting main header", FL); result = OLE_get_header( ole ); if (result != 0) return result; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Converting main header", FL); result = OLE_convert_header( ole ); if (result != 0) return result; result = OLE_header_sanity_check( ole ); if (result > 0) return OLEER_INSANE_OLE_FILE; DOLE OLE_print_header( ole ); DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading FAT", FL); result = OLE_load_FAT( ole ); if (result != 0) return result; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading miniFAT chain", FL); ole->miniFAT = OLE_load_chain( ole, ole->header.mini_fat_start ); if (ole->miniFAT == NULL) return OLEER_MINIFAT_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading Directory stream chain", FL); ole->properties = OLE_load_chain( ole, ole->header.directory_stream_start_sector ); if (ole->properties == NULL) return OLEER_PROPERTIES_READ_FAIL; i=0; current_property = ole->properties; property_limit = current_property +ole->last_chain_size ; // while(1) while (current_property < property_limit) { struct OLE_directory_entry a_dir_object, *adir; int property_value=0; adir = &a_dir_object; OLE_dir_init(adir); property_value = get_1byte_value(current_property); if (property_value < 1) break; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG:--------- DIRECTORY INDEX: %d",FL,i); OLE_convert_directory( ole, current_property, adir ); DOLE { LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Printing directory details...",FL); OLE_print_directory( ole, adir); LOGGER_log("%s:%d:OLE_decode_file:DEBUG: End of directory details",FL); } if (adir->element_colour > 1) break; if ((adir->element_type == STGTY_INVALID)||(adir->element_type > STGTY_ROOT)) { DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: breaking out due to element type %d",FL, adir->element_type); break; } else if (adir->element_type == STGTY_ROOT){ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading ministream/SmallBlockArray",FL); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: ministream done",FL); } else if (adir->element_type == STGTY_STORAGE) { /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Item is directory, start child is at index %d\n",FL,i); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: DIRECTORY ministream done",FL); } else if (adir->element_type == STGTY_STREAM) { /** STREAM ELEMENT **/ /** STREAM ELEMENT **/ /** STREAM ELEMENT **/ OLE_decode_stream( ole, adir, decode_path ); } else { /** If the element isn't of the above types then it's possibly ** an empty element or just one used for the MSAT/SAT ** either way we just step over it and carry on **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Element type %d does not need to be handled",FL,adir->element_type); } // Jump to the next property record, which // is always 128 bytes ahead. current_property += 128; i++; } // While there are still more directory entries to read in. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Finished",FL); /* //if (ole->f) fclose(ole->f); fclose(ole->f); if (ole->FAT) free(ole->FAT); if (ole->miniFAT) free(ole->miniFAT); if (ole->ministream) free(ole->ministream); if (ole->properties) free(ole->properties); */ return OLE_OK; } p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/ole.h0000644000175000001440000001120110347164455016075 0ustar jlaiusers #ifndef LIBOLE #define LIBOLE #define LIBOLE_VERSION "200512112226" #define OLE_OK 0 #define OLEER_NO_INPUT_FILE 100 #define OLEER_BAD_INPUT_FILE 101 #define OLEER_NOT_OLE_FILE 102 #define OLEER_INSANE_OLE_FILE 103 #define OLEER_DECODE_NULL_OBJECT 10 #define OLEER_DECODE_NULL_FILENAME 11 #define OLEER_DECODE_NULL_PATH 12 #define OLEER_MINIFAT_READ_FAIL 30 #define OLEER_PROPERTIES_READ_FAIL 31 #define OLEER_MINISTREAM_READ_FAIL 32 #define OLEER_MINISTREAM_STREAM_READ_FAIL 33 #define OLEER_NORMALSTREAM_STREAM_READ_FAIL 34 #define OLEER_GET_BLOCK_SEEK 41 #define OLEER_GET_BLOCK_READ 42 #define OLEER_MEMORY_OVERFLOW 50 #define OLE_VERBOSE_NORMAL 1 #define OLE_VERBOSE_FATREAD 2 #define OLE_VERBOSE_DIRREAD 4 #define OLE_VERBOSE_STREAMREAD 8 #define OLE_VERBOSE_STREAMDECODE 16 #define OLE_VNORMAL(x) ((x) && OLE_VERBOSE_NORMAL == OLE_VERBOSE_NORMAL ) #define OLE_DEBUG_NORMAL 1 #define OLE_DEBUG_PEDANTIC 2 #define OLE_DNORMAL(x) ((x) && OLE_DEBUG_NORMAL == OLE_DEBUG_NORMAL) #define OLE_DPEDANTIC(x) ((x) && OLE_DEBUG_PEDANTIC == OLE_DEBUG_PEDANTIC) #define OLE_HEADER_FAT_SECTOR_COUNT_LIMIT 109 struct OLE_header { unsigned int minor_version; unsigned int dll_version; unsigned int byte_order; unsigned int sector_shift; unsigned int sector_size; unsigned int mini_sector_shift; unsigned int mini_sector_size; unsigned int fat_sector_count; unsigned int directory_stream_start_sector; unsigned int mini_cutoff_size; unsigned int mini_fat_start; unsigned int mini_fat_sector_count; unsigned int dif_start_sector; unsigned int dif_sector_count; unsigned int FAT[OLE_HEADER_FAT_SECTOR_COUNT_LIMIT]; }; #define OLE_DIRECTORY_ELEMENT_NAME_SIZE 64 #define OLE_DIRECTORY_CLASS_SIZE 16 #define OLE_DIRECTORY_TIMESTAMPS_SIZE 16 struct OLE_directory_entry { char element_name[OLE_DIRECTORY_ELEMENT_NAME_SIZE]; int element_name_byte_count; char element_type; char element_colour; unsigned int left_child; unsigned int right_child; unsigned int root; unsigned char class[OLE_DIRECTORY_CLASS_SIZE]; unsigned int userflags; unsigned char timestamps[OLE_DIRECTORY_TIMESTAMPS_SIZE]; unsigned int start_sector; unsigned int stream_size; }; #define OLE_HEADER_BLOCK_SIZE 512 struct OLE_object { int error; size_t file_size; int last_sector; size_t last_chain_size; FILE *f; unsigned char *FAT; unsigned char *FAT_limit; /** Added to prevent segment violations **/ unsigned char *miniFAT; unsigned char *miniFAT_limit; /** Added to prevent segment violations **/ unsigned char header_block[OLE_HEADER_BLOCK_SIZE]; unsigned char *ministream; unsigned char *properties; struct OLE_header header; // End user configurable parameters: int debug; int verbose; int quiet; int save_unknown_streams; int save_streams; int save_mini_streams; int save_normal_streams; int decode_streams; int decode_mini_streams; int decode_normal_streams; int (*filename_report_fn)(char *); }; // Prototypes int OLE_version( void ); int OLE_init( struct OLE_object *ole ); int OLE_set_verbose( struct OLE_object *ole, int level ); int OLE_set_debug( struct OLE_object *ole, int level ); int OLE_set_quiet( struct OLE_object *ole, int level ); int OLE_set_save_unknown_streams( struct OLE_object *ole, int level ); int OLE_get_block( struct OLE_object *ole, int block_index, unsigned char *block_buffer ); int OLE_get_miniblock( struct OLE_object *ole, int block_index, unsigned char *block_buffer ); int OLE_dbstosbs( char *raw_string, size_t char_count, char *clean_string, int clean_string_len ); int OLE_print_string( char *string, size_t char_count); int OLE_print_sector( struct OLE_object *ole, char *sector, unsigned int bytes); int OLE_get_header( struct OLE_object *ole ); int OLE_convert_header( struct OLE_object *ole ); int OLE_print_header( struct OLE_object *ole ); int OLE_convert_directory(struct OLE_object *ole, unsigned char *buf, struct OLE_directory_entry *dir ); int OLE_print_directory( struct OLE_object *ole, struct OLE_directory_entry *dir ); int OLE_load_FAT( struct OLE_object *ole ); int OLE_follow_chain( struct OLE_object *ole, int FAT_sector_start ); int OLE_follow_minichain( struct OLE_object *ole, int miniFAT_sector_start ); unsigned char *OLE_load_minichain( struct OLE_object *ole, int miniFAT_sector_start ); unsigned char *OLE_load_chain( struct OLE_object *ole, int FAT_sector_start ); int OLE_open_file( struct OLE_object *ole, char *fullpath ); int OLE_decode_file( struct OLE_object *ole, char *fname, char *decode_path ); int OLE_decode_file_done( struct OLE_object *ole ); // Our callbacks. int OLE_set_filename_report_fn( struct OLE_object *ole, int (*ptr_to_fn)(char *) ); #endif p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/README0000644000175000001440000000066010347164455016034 0ustar jlaiusers--------- 2005-December-12 ripOLE has now started to become a useful tool in its own right, though it is more directly being developed for incorporation into ripMIME and subsequently Xamime. pldaniels@pldaniels.com --------- To check for [and save] attachments in a MS Office file, simply do the following: ./ripole -i officefile.doc -v -d tmp ..and ripOLE will decode the attachments to the 'tmp' directory -END. p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/olestream-unwrap.c0000644000175000001440000002312610347164455020627 0ustar jlaiusers#include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "bytedecoders.h" #include "olestream-unwrap.h" #define DUW if (oleuw->debug) struct OLE10_header{ unsigned char data[6]; char *attach_name; unsigned char data2[8]; char *fname_1; char *fname_2; size_t attach_size; size_t attach_size_1; size_t attach_start_offset; }; /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_init Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_init( struct OLEUNWRAP_object *oleuw ) { oleuw->debug = 0; oleuw->verbose = 0; oleuw->filename_report_fn = NULL; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_debug Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_debug( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->debug = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_verbose Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_verbose( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->verbose = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_save_unknown_streams Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_save_unknown_streams( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->save_unknown_streams = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_save_stream Returns Type : int ----Parameter List 1. char *fname, 2. char *stream, 3. size_t bytes , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_save_stream( struct OLEUNWRAP_object *oleuw, char *fname, char *decode_path, char *stream, size_t bytes ) { char *full_name; FILE *f; int result = 0; DUW LOGGER_log("%s:%d:OLEUNWRAP_save_stream:DEBUG: fname=%s, decodepath=%s, size=%ld" ,FL ,fname ,decode_path ,bytes ); full_name = PLD_dprintf("%s/%s", decode_path, fname ); if (full_name == NULL) { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:ERROR: Unable to create filename string from '%s' and '%s'",FL,fname,decode_path); return -1; } f = fopen(full_name,"w"); if (f != NULL) { size_t write_count; write_count = fwrite( stream, 1, bytes, f ); if (write_count != bytes) { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:WARNING: Only wrote %d of %d bytes to file %s\n",FL, write_count, bytes, full_name ); } fclose(f); } else { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:ERROR: Unable to open %s for writing (%s)\n",FL,full_name, strerror(errno)); result = -1; } if (full_name) free(full_name); DUW LOGGER_log("%s:%d:OLEUNWRAP_save_stream:DEBUG: Done saving '%s'",FL, fname); return result; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_sanitize_filename Returns Type : int ----Parameter List 1. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_sanitize_filename( char *fname ) { while (*fname) { if( !isalnum((int)*fname) && (*fname != '.') ) *fname='_'; if( (*fname < ' ')||(*fname > '~') ) *fname='_'; fname++; } return 0; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_decode_attachment Returns Type : int ----Parameter List 1. char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_decode_attachment( struct OLEUNWRAP_object *oleuw, char *stream, size_t stream_size, char *decode_path ) { struct OLE10_header oh; char *sp = stream; char *data_start_point = stream; int result = OLEUW_OK; // Get the data size oh.attach_size_1 = (size_t)get_4byte_value( sp ); sp += 4; DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: attachsize = %d, stream length = %d\n", FL, oh.attach_size_1, stream_size ); oh.attach_start_offset = (stream_size -oh.attach_size_1); data_start_point = stream +oh.attach_start_offset; //if (oh.attach_start_offset == 4) if (oh.attach_start_offset < 4) { // If we only had the stream byte-lenght in our header // then we know we don't have a complex header. oh.attach_name = PLD_dprintf("unknown-%ld",oh.attach_size_1); oh.attach_size = oh.attach_size_1; } else { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Decoding file information header",FL); // Unknown memory segment memcpy( oh.data, sp, 2 ); sp += 2; // Full attachment string oh.attach_name = strdup( sp ); sp = sp + strlen(oh.attach_name) +1; // Attachment full path oh.fname_1 = strdup( sp ); sp += strlen(oh.fname_1) +1; // Unknown memory segment memcpy( oh.data2, sp, 8 ); sp = sp +8; // Attachment full path oh.fname_2 = strdup( sp ); sp += strlen(oh.fname_2) +1; oh.attach_size = (size_t)get_4byte_value( sp ); sp += 4; if (oh.attach_size > stream_size) oh.attach_size = stream_size; data_start_point = sp; } DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Attachment %s:%s:%s size = %d\n",FL, oh.attach_name, oh.fname_1, oh.fname_2, oh.attach_size ); /** 20050119:2053:PLD - Added to sanitize 8-bit filenames **/ /** Sanitize the output filename **/ OLEUNWRAP_sanitize_filename(oh.attach_name); OLEUNWRAP_sanitize_filename(oh.fname_1); OLEUNWRAP_sanitize_filename(oh.fname_2); result = OLEUNWRAP_save_stream( oleuw, oh.attach_name, decode_path, data_start_point, oh.attach_size ); if (result == OLEUW_OK) { if (oleuw->debug > 0) LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Calling reporter for the filename",FL); if ((oleuw->verbose > 0)&&(oleuw->filename_report_fn != NULL)) { oleuw->filename_report_fn(oh.attach_name); } // Do call back to reporting function } // Clean up our previously allocated data if (oh.fname_1 != NULL) free(oh.fname_1); if (oh.attach_name != NULL) free(oh.attach_name); if (oh.fname_2 != NULL) free(oh.fname_2); return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_decodestream Returns Type : int ----Parameter List 1. char *element_string, 2. char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_decodestream( struct OLEUNWRAP_object *oleuw, char *element_string, char *stream, size_t stream_size, char *decode_path ) { int result = OLEUW_OK; if (strstr(element_string, OLEUW_ELEMENT_10NATIVE_STRING) != NULL) { OLEUNWRAP_decode_attachment( oleuw, stream, stream_size, decode_path ); } else { if (oleuw->debug) LOGGER_log("Unable to decode stream with element string '%s'\n", element_string); result = OLEUW_STREAM_NOT_DECODED; } return result; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_filename_report_fn Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int (*ptr_to_fn)(char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_filename_report_fn( struct OLEUNWRAP_object *oleuw, int (*ptr_to_fn)(char *) ) { oleuw->filename_report_fn = ptr_to_fn; return 0; } p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/olestream-unwrap.h0000644000175000001440000000200110347164455020621 0ustar jlaiusers #define OLEUW_ELEMENT_10NATIVE 10 #define OLEUW_ELEMENT_10NATIVE_STRING "Ole10Native" #define OLEUW_OK 0 #define OLEUW_STREAM_NOT_DECODED 100 struct OLEUNWRAP_object { int (*filename_report_fn)(char *); int debug; int verbose; int save_unknown_streams; }; int OLEUNWRAP_init( struct OLEUNWRAP_object *oleuw ); int OLEUNWRAP_set_debug( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_set_verbose( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_set_save_unknown_streams( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_save_stream( struct OLEUNWRAP_object *oleuw, char *fname, char *decode_path, char *stream, size_t bytes ); int OLEUNWRAP_decode_attachment( struct OLEUNWRAP_object *oleuw, char *stream, size_t stream_size, char *decode_path ); int OLEUNWRAP_decodestream( struct OLEUNWRAP_object *oleuw, char *element_string, char *stream, size_t stream_size, char *decode_path ); int OLEUNWRAP_set_filename_report_fn( struct OLEUNWRAP_object *oleuw, int (*ptr_to_fn)(char *) ); p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/CHANGELOG0000644000175000001440000001131110347164455016361 0ustar jlaiusers -----------------------------------ripOLE--------------------- 20051212-1100:PLD: ---Version 0.2.0 RELEASE Fixed up OLE Stream decoding - attachments now come out with the stream headers correctly stripped off. FINALLY! Reverted the sector-ID handling back to a SIGNED 4-byte integer, as negative values are required to signal special sector ID's. 20050315-1544:PLD: Added last-sector check sanitisation while stepping along FAT/miniFAT streams 20050119-2108:PLD: Added filename sanitization, so that obscure 8-bit values didn't get used. See OLEUNWRAP_sanitize_filename() 20041127-2051:PLD: Added sanity checking for the OLE header, see the fn OLE_header_sanity_check(). If the OLE file fails the sanity check, a return code of 103 will be issued. 2004-Oct-18: Changed unsigned int sectors to signed in OLE_follow_minichain() so that negative sector requests could be detected and prevent segfaults. Thanks to Gabriele Cariole for supplying the offending mailpack. 2004-Sep-30: Added a loop detection system into OLE_follow_chain to detect situations where the loop isn't tight (ie, if it goes A->B->C->D->A). This requires two new files to be added to the archive (bt-int.[ch]) Thanks to Brian for offering the mailpack which exposed this condition. 2004-Sep-25: Fixed old style fprintf(stdout) logging calls. Removed exit(1) calls. Fixed up memory leak in OLE_get_block(), where bb would not be explicitly free'd on either errors or normal exit. 2004-Aug-16: Added ole->FAT_limit into the general OLE struct so that when traversing the FAT chain later, it can be possible to check for requests beyond the memory block limit (which would other- wise cause segfaults) 2004-Jul-17: Fixed up unwanted verbosity about read mismatch in ole.c:321 Added macro VOLE which is 'if (ole->verbose)'. Fixed up unwanted verbosity from ole when a negative sector read request is made and not in verbosity mode. (ole.c:891) 2004-May-22: Fixed up minifat walk routines, made current_sector comparisons use hex notation rather than assumed signed int comparison. Added start section sanity checking with minifat walker. ie, ensuring that the starting sector is infact greater than zero 2004-May-21: Fixed up various memory leaks which were due to premature function exits, but without cleaning up any preallocated memory blocks. 2004-Apr-5: 19H50:PLD:REL Released as v0.1.3 19H48:PLD:DEV Added 'help' output for when no paramters are given. 14H22:PLD:DEV Added stream size sanity checking when decoding the attachment size in olestream-unwrap which would occasionally generate some rather unlrealistc stream sizes. OLEUNWRAP_decode_attachment(); 01H21:PLD:DEV Finally fixed occasional segfault bug with some OLE2 files. Fix was to define the memory boundary limit of the ole properties field and check on each pass of the loop that it wasn't being breached. 2003-Nov-26: 20H13:PLD:DEV Release 0.0.4 Fixed up some potential logging segfaults if used in conjunction with syslog output. 2003-Nov-17: 16H20:PLD:DEV Added bounds checking to entire processing system so that it cannot request a sector outside of the real limits of the loaded OLE file. I consider this only to be a partial fix to a problem of various documents which do not seem to be able to be decoded reliably, fortunately at least the bounds checking stops the segfaults. 2003-Oct-31: 07H38:PLD:DEV Added -q (quiet) option, which silences various non-fatal error messages. 2003-Aug-28: 22H04:PLD:DEV Added bounds checking on Mini-FAT loader, to ensure it would not attempt to load up a FAT chain from a negative starting sector. 2003-Jul-16: 21H23:PLD:DEV Added bounds check on chain follower/loader so that it would not attempt to seek from a starting sector < 0 2003-Jul-11: 19H53:PLD:DEV Added bounds checking to OLE_dbstosbs() 2003-Jul-05: 01H18:PLD:DEV Added --save-unknown-streams facility, this allows saving of streams which ripOLE doesn't yet know how to explicitly handle. 2003-Jul-04: 20H52:PLD:DEV Posted to Freshmeat Applied small patch to stop compile warnings about bp and buffer pointers being used (possibly) uninitialised. 08H13:PLD:DEV Fixed segfault in code when extended-FAT/BAT/DIF was required. This was caused by the header-conversion fn because it did not stop attempting to copy sectors beyond the first 109. 00H10:PLD:DEV: Fixed all 3 FAT modes, mini, standard and extended. All files now decode assuming they have the 'Ole10Native' element-name 2003-Jul-03: Initial code release, not currently usable to extract files but the code does dump the OLE streams to file, in which you can see the attachment information. p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/bytedecoders.c0000644000175000001440000000366510347164455020004 0ustar jlaiusers#include /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_byte_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_1byte_value( char *start ) { return (int) *start; } /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_ushort_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_2byte_value( char *start ) { int value = 0; value = (unsigned char)*start | (((unsigned char)*(start +1)) << 8); return value; } /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_ulong_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_4byte_value( char *start ) { int value = 0; value = (int)((unsigned char)*start) |(((unsigned char)*(start +1)) << 8) |(((unsigned char)*(start +2)) << 16) |(((unsigned char)*(start +3)) << 24); // printf("String=0x%x %x %x %x:%u = %d\n", *start, *(start +1), *(start +2), *(start +3), *(start +3), value); return value; } p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/bytedecoders.h0000644000175000001440000000015410347164455017777 0ustar jlaiusersint get_1byte_value( char *start ); int get_2byte_value( char *start ); int get_4byte_value( char *start ); p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/ripole.c0000644000175000001440000002045010347164455016611 0ustar jlaiusers // Microsoft OLE2 stream parser. #include #include #include #include #include "logger.h" #include "pldstr.h" #include "ole.h" struct ripOLE_object { int debug; int verbose; int save_unknown_streams; char *inputfile; char *outputdir; }; #define ROLE_VERSION "0.2.0" static char defaultdir[]="."; static char version[]="0.2.0 - 12-December-2005 (C) PLDaniels http://www.pldaniels.com/ripole"; static char help[]="ripOLE -i [ -d ] [--save-unknown-streams] [--version|-V] [--verbose|-v] [--debug] [--help|-h]"; /*-----------------------------------------------------------------\ Function Name : set_defaults Returns Type : int ----Parameter List 1. struct ripOLE_object *role , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_set_defaults( struct ripOLE_object *role ) { role->outputdir = defaultdir; role->debug = 0; role->verbose = 0; role->save_unknown_streams = 0; role->inputfile = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : parse_parameters Returns Type : int ----Parameter List 1. struct ripOLE_object *role, 2. int argc, 3. char **argv , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_parse_parameters( struct ripOLE_object *role, int argc, char **argv ) { int i; int result = 0; role->outputdir = defaultdir; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'i': i++; role->inputfile = strdup(argv[i]); break; case 'd': i++; role->outputdir = strdup(argv[i]); break; case 'v': role->verbose = 1; break; case 'V': fprintf (stdout, "%s\n", version); exit (1); break; case 'h': fprintf (stdout, "%s\n", help); exit (1); break; // if we get ANOTHER - symbol, then we have an extended flag case '-': if (strncmp (&(argv[i][2]), "verbose", 7) == 0) { role->verbose=1; } else if (strncmp (&(argv[i][2]), "save-unknown-streams", 20) == 0) { role->save_unknown_streams = 1; } else if (strncmp (&(argv[i][2]), "debug", 5) == 0) { role->debug=1; } else if (strncmp (&(argv[i][2]), "version", 7) == 0) { fprintf (stdout, "%s\n", version); exit (1); } else if (strncmp(&(argv[i][2]),"help",4)==0) { fprintf(stdout,"%s\n",help); exit(1); } else { fprintf(stdout,"Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } break; // else, just dump out the help message default: fprintf(stdout,"Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } // Switch argv[i][1] } // if argv[i][0] == - } // for return result; } /*-----------------------------------------------------------------\ Function Name : set_parameters Returns Type : int ----Parameter List 1. struct ripOLE_object *role, 2. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_set_parameters( struct ripOLE_object *role, struct OLE_object *ole ) { if(role->debug == 1) { OLE_set_debug(ole, OLE_DEBUG_NORMAL); } if (role->verbose == 1) { OLE_set_verbose(ole, OLE_VERBOSE_NORMAL); } if (role->save_unknown_streams == 1) { OLE_set_save_unknown_streams(ole, 1); } return 0; } /*-----------------------------------------------------------------\ Function Name : ripOLE_report_filename_decoded Returns Type : int ----Parameter List 1. char *filename, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_report_filename_decoded(char *filename) { LOGGER_log("Decoding filename=%s", filename); return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_init Returns Type : int ----Parameter List 1. struct ripOLE_object *role, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_init(struct ripOLE_object *role) { role->debug = 0; role->verbose = 0; role->save_unknown_streams = 0; role->inputfile = NULL; role->outputdir = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_done Returns Type : int ----Parameter List 1. struct ripOLE_object *role, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_done(struct ripOLE_object *role) { if (role->inputfile != NULL) free(role->inputfile); if (role->outputdir != NULL) free(role->outputdir); return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_validate Returns Type : int ----Parameter List 1. struct ripOLE_object *role , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_validate(struct ripOLE_object *role ) { int result = 0; if (role->inputfile == NULL) { fprintf(stderr,"ripOLE requires an input file to decode\n"); return -1; } return result; } /*-----------------------------------------------------------------\ Function Name : main Returns Type : int ----Parameter List 1. int argc, 2. char **argv , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int main( int argc, char **argv ) { struct ripOLE_object role; struct OLE_object *ole = NULL; int result = 0; if (argc == 1) { fprintf (stdout, "%s\n", help); exit(1); } ole = malloc(sizeof(struct OLE_object)); if (ole == NULL) { LOGGER_log("ripOLE: Cannot allocate memory for OLE object"); return 1; } LOGGER_set_output_mode(_LOGGER_STDOUT); OLE_init(ole); ROLE_init(&role); ROLE_set_defaults( &role ); ROLE_parse_parameters(&role, argc, argv); result = ROLE_validate(&role); if (result != 0) return result; ROLE_set_parameters(&role, ole); OLE_set_filename_report_fn(ole, ROLE_report_filename_decoded ); result = OLE_decode_file( ole, role.inputfile, role.outputdir ); OLE_decode_file_done(ole); if ((result != 0)) { if (role.verbose) { switch (result) { case OLEER_NO_INPUT_FILE: case OLEER_BAD_INPUT_FILE: LOGGER_log("Cannot locate input file '%s'",role.inputfile); break; case OLEER_NOT_OLE_FILE: LOGGER_log("File '%s' is not OLE2 format",role.inputfile); break; case OLEER_INSANE_OLE_FILE: LOGGER_log("OLE input file '%s' is insane", role.inputfile); break; default: LOGGER_log("ripOLE: decoding of %s resulted in error %d\n", role.inputfile, result ); } } return result; } if (ole != NULL) free(ole); ROLE_done(&role); return result; } /** end of ripOLE **/ p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/bt-int.c0000644000175000001440000000254010347164455016514 0ustar jlaiusers#include #include #include "bt-int.h" int BTI_init( struct bti_node **n ) { *n = NULL; return 0; } int BTI_add( struct bti_node **n, int value ) { int collision = 0; int dir = 0; struct bti_node *p = NULL, *node = *n; // fprintf(stdout,"Adding %d to %p\n", value, *n); while (node != NULL) { if (value > node->data) { p = node; dir=1; node = node->r; } else if (value < node->data) { p = node; dir=-1; node = node->l; } else if (value == node->data) { collision = 1; break; } } if (collision == 0) { struct bti_node *leaf; leaf = malloc(sizeof(struct bti_node)); if (leaf == NULL) { return -1; } leaf->data = value; leaf->l = leaf->r = NULL; if (p != NULL) { switch (dir) { case 1: p->r = leaf; break; case -1: p->l = leaf; break; } } else { *n = leaf; } } return collision; } int BTI_dump( struct bti_node **n ) { struct bti_node *node; node = *n; if (node->l) BTI_dump(&(node->l)); if (*n) { fprintf(stdout,"%d, ", node->data); } if (node->r) BTI_dump(&(node->r)); return 0; } int BTI_done( struct bti_node **n ) { struct bti_node *node; if (n == NULL) return 0; if (*n == NULL) return 0; node = *n; if (node->l) BTI_done(&(node->l)); if (node->r) BTI_done(&(node->r)); if (*n) { free(*n); *n = NULL; } return 0; } p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/bt-int.h0000644000175000001440000000040610347164455016520 0ustar jlaiusers#ifndef __BT_INT__ #define __BT_INT__ struct bti_node { int data; struct bti_node *l,*r; }; int BTI_init( struct bti_node **n ); int BTI_add( struct bti_node **n, int value ); int BTI_dump( struct bti_node **n ); int BTI_done( struct bti_node **n ); #endif p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/logger.c0000644000175000001440000001705310347164455016603 0ustar jlaiusers // Abstract logging system used to facilitate multiple modes // of logging #include #include #include #ifndef WIN32 #include #endif #include #include #include #include "logger.h" #ifndef WIN32 static int _LOGGER_mode = _LOGGER_SYSLOG; static int _LOGGER_syslog_mode = LOG_MAIL|LOG_INFO; #else static int _LOGGER_mode = _LOGGER_STDERR; static int _LOGGER_syslog_mode = 0; #endif static FILE *_LOGGER_outf; struct LOGGER_globals { int wrap; int wraplength; }; // Create and Initialise the global structure for LOGGER, // we init it to have NO wrapping. static struct LOGGER_globals LOGGER_glb={ 0, 0 }; /*------------------------------------------------------------------------ Procedure: LOGGER_get_file ID:1 Purpose: Returns the pointer to the file being used to output logs to Input: Output: Errors: ------------------------------------------------------------------------*/ FILE *LOGGER_get_file( void ) { return _LOGGER_outf; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_mode ID:1 Purpose: Sets the message/log output method, ie, stderr, stdout or syslog Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_mode( int modechoice ) { _LOGGER_mode = modechoice; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_file ID:1 Purpose: Sets the output file for when _LOGGER_mode is set to _LOGGER_file Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_file( FILE *f ) { _LOGGER_outf = f; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_syslog_mode ID:1 Purpose: Sets the mode that messaging to the syslog daemon will be sent as (ie, LOG_MAIL|LOG_INFO) Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_syslog_mode( int syslogmode ) { _LOGGER_syslog_mode = syslogmode; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_logfile ID:1 Purpose: Opens and setups the internal Log file file pointer with the log file as given by lfname Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_logfile( char *lfname ) { int result = 0; _LOGGER_outf = fopen(lfname,"a"); if (!_LOGGER_outf) { #ifndef WIN32 syslog(1,"LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)",lfname,strerror(errno)); #else fprintf(stderr, "LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)\n", lfname, strerror(errno)); #endif result = -1; } return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wraplength ID:1 Purpose: Sets the character count at which LOGGER will break a line Input: int length: Positive integer indicating number of chracters at which to wrap at Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wraplength( int length ) { if ( length >= 0 ) { LOGGER_glb.wraplength = length; } return LOGGER_glb.wraplength; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wrap ID:1 Purpose: Set log output wrapping to on or off Input: int level: 0 = no wrap, > 0 = wrap. Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wrap( int level ) { if ( level >= 0 ) { LOGGER_glb.wrap = level; } return LOGGER_glb.wrap; } /*------------------------------------------------------------------------ Procedure: LOGGER_close_logfile ID:1 Purpose: Closes the modules log file pointer. Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_close_logfile( void ) { int result = 0; if (_LOGGER_outf) fclose(_LOGGER_outf); return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_clean_output ID:1 Purpose: Checks through the output string for any characters which could cause potential 'isssues' with the data writing calls, items such as stray non-escaped % characters can cause havoc. Input: char *string: Raw string int maxsize: Maximum available buffer size for this string to expand to Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_clean_output( char *string, char **buffer ) { char *newstr; char *p, *q; char *next_space; int pc; int slen = strlen( string ); int line_size; int maxsize = slen *2; // First up, allocate maxsize bytes for a temporary new string. newstr = malloc(slen *2 +1); if ( newstr == NULL ) { // FIXME - Report an error here ... to -somewhere- return -1; } p = newstr; q = string; pc = 0; line_size = 0; while (slen--) { // Do we need to apply any wrapping to the output? If so then we // shall embark on a journey of strange space and distance // evaluations to determine if we should wrap now or later if ( LOGGER_glb.wrap > 0 ) { if (isspace((int)*q)) { next_space = strpbrk( (q+1), "\t\r\n\v " ); if (next_space != NULL) { if ((line_size +(next_space -q)) >= LOGGER_glb.wraplength) { *p = '\n'; p++; pc++; line_size = 0; } } } if ( line_size >= LOGGER_glb.wraplength ) { *p = '\n'; p++; pc++; line_size = 0; } } // If the string has a % in it, then we need to encode it as // a DOUBLE % symbol. if (*q == '%') { // if (strchr("fdlsxXn",*(q+1))) // { *p = '%'; p++; pc++; // } } // Copy the character of the string in *p = *q; // Move everything along. q++; p++; pc++; line_size++; if ( pc > (maxsize -1) ) { break; } } *p = '\0'; // This will have to be deallocated later! if (newstr) *buffer = newstr; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_log ID:1 Purpose: Logs the params as supplied to the required output as defined by LOGGER_set_output Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_log( char *format, ...) { va_list ptr; char tmpoutput[10240]; char linebreak[]="\n"; char nolinebreak[]=""; char *lineend; char *output; // get our variable arguments va_start(ptr,format); // produce output, and spit to the log file #ifdef NO_SNPRINTF vsprintf(tmpoutput, format, ptr); #else vsnprintf(tmpoutput,10240,format,ptr); #endif LOGGER_clean_output( tmpoutput, &output ); if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } // Send the output to the appropriate output destination switch (_LOGGER_mode) { case _LOGGER_STDERR: fprintf(stderr,"%s%s",output, lineend ); break; case _LOGGER_SYSLOG: syslog(_LOGGER_syslog_mode,output); break; case _LOGGER_STDOUT: fprintf(stdout,"%s%s",output, lineend); fflush(stdout); break; case _LOGGER_FILE: fprintf(_LOGGER_outf,"%s%s",output,lineend); fflush(_LOGGER_outf); break; default: fprintf(stdout,"LOGGER-Default: %s%s",output,lineend); } if (output) free(output); return 0; } p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/logger.h0000644000175000001440000000110510347164455016577 0ustar jlaiusers #ifndef __LOGGER__ #define __LOGGER__ // LOGGER.h // #define _LOGGER_STDERR 1 #define _LOGGER_STDOUT 2 #define _LOGGER_FILE 3 #ifndef WIN32 #define _LOGGER_SYSLOG 4 #endif #ifndef FL #define FL __FILE__,__LINE__ #endif int LOGGER_log( char *format, ...); int LOGGER_set_output_mode( int modechoice ); int LOGGER_set_output_file( FILE *f ); int LOGGER_set_syslog_mode( int syslogmode ); int LOGGER_set_logfile( char *lfname ); int LOGGER_set_wraplength( int length ); int LOGGER_set_wrap( int level ); int LOGGER_close_logfile( void ); FILE *LOGGER_get_file( void ); #endif p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/INSTALL0000644000175000001440000000005510347164455016203 0ustar jlaiusers -----INSTALL make ./ripole your-ole.doc p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/pldstr.c0000644000175000001440000005154710347164455016642 0ustar jlaiusers #include #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" /*-----------------------------------------------------------------\ Function Name : *PLD_strstr Returns Type : char ----Parameter List 1. char *haystack, 2. char *needle, 3. int insensitive, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strstr(char *haystack, char *needle, int insensitive) { char *hs, *ne; char *result; // LOGGER_log("%s:%d:\nHS=%s\nNE=%s\nIS=%d\n",FL, haystack, needle, insensitive ); if (insensitive > 0) { hs = strdup(haystack); PLD_strlower(hs); ne = strdup(needle); PLD_strlower(ne); } else { hs = haystack; ne = needle; } result = strstr(hs, ne); // if (result) LOGGER_log("%s:%d:HIT: %s",FL, result); // else LOGGER_log("%s:%d:MISS (looking for %s|%s)",FL, needle,ne); if ((result != NULL)&&(insensitive > 0)) { result = result -hs +haystack; free(hs); free(ne); // LOGGER_log("%s:%d:HIT - %s",FL, result ); } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strncpy ID:1 Purpose: Copy characters from 'src' to 'dst', writing not more than 'len' characters to the destination, including the terminating \0. Thus, for any effective copying, len must be > 1. Input: char *dst: Destination string char *src: Source string size_t len: length of string Output: Returns a pointer to the destination string. Errors: ------------------------------------------------------------------------*/ char *PLD_strncpy (char *dst, const char *src, size_t len) { // Thanks go to 'defrost' of #c for providing the replacement // code which you now see here. It covers the errors better // than my own previous code. // If we have no buffer space, then it's futile attempting // to copy anything, just return NULL if (len==0) return NULL; // Providing our destination pointer isn't NULL, we can // commence copying data across if (dst) { char *dp = dst; // If our source string exists, start moving it to the // destination string character at a time. if (src) { char *sp = (char *)src; while ((--len)&&(*sp)) { *dp=*sp; dp++; sp++; } } *dp='\0'; } return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncat ID:1 Purpose: Buffer size limited string concat function for two strings. Input: char *dst: Destination string char *src: Source string size_t len: Destination string buffer size - total string size cannot exceed this Output: Errors: If the length of both strings in total is greater than the available buffer space in *dst, we copy the maximum possible amount of chars from *src such that buffer does not overflow. A suffixed '\0' will always be appended. ------------------------------------------------------------------------*/ char *PLD_strncat( char *dst, const char *src, size_t len ) { char *dp = dst; const char *sp = src; size_t cc; if (len == 0) return dst; len--; // Locate the end of the current string. cc = 0; while ((*dp)&&(cc < len)) { dp++; cc++; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncate ID:1 Purpose: Catencates a source string to the destination string starting from a given endpoint. This allows for faster catencation of strings by avoiding the computation required to locate the endpoint of the destination string. Input: char *dst: Destination string char *src: Source string size_t len: Destination buffer size char *endpoint: Endpoint of destination string, location from where new string will be appended Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ) { char *dp = dst; const char *sp = src; size_t cc = 0; if (len == 0) return dst; len--; // If endpoint does not relate correctly, then force manual detection // of the endpoint. if ((!endpoint)||(endpoint == dst)||((endpoint -dst +1)>(int)len)) { // Locate the end of the current string. cc = 0; while ((*dp != '\0')&&(cc < len)) { dp++; cc++; } } else { cc = endpoint -dst +1; dp = endpoint; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: XAM_strncasecmp ID:1 Purpose: Portable version of strncasecmp(), this may be removed in later versions as the strncase* type functions are more widely implemented Input: Output: Errors: ------------------------------------------------------------------------*/ int PLD_strncasecmp( char *s1, char *s2, int n ) { char *ds1 = s1, *ds2 = s2; char c1, c2; int result = 0; while(n > 0) { c1 = tolower(*ds1); c2 = tolower(*ds2); if (c1 == c2) { n--; ds1++; ds2++; } else { result = c2 - c1; n = 0; } } return result; } /*------------------------------------------------------------------------ Procedure: XAM_strtok ID:1 Purpose: A thread safe version of strtok() Input: Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ) { char *stop; char *dc; char *result = NULL; if ( line ) { st->start = line; } //Strip off any leading delimeters dc = delimeters; while ((st->start)&&(*dc != '\0')) { if (*dc == *(st->start)) { st->start++; dc = delimeters; } else dc++; } // Where we are left, is the start of our token. result = st->start; if ((st->start)&&(st->start != '\0')) { stop = strpbrk( st->start, delimeters ); /* locate our next delimeter */ // If we found a delimeter, then that is good. We must now break the string here // and don't forget to store the character which we stopped on. Very useful bit // of information for programs which process expressions. if (stop) { // Store our delimeter. st->delimeter = *stop; // Terminate our token. *stop = '\0'; // Because we're emulating strtok() behaviour here, we have to // absorb all the concurrent delimeters, that is, unless we // reach the end of the string, we cannot return a string with // no chars. stop++; dc = delimeters; while (*dc != '\0') { if (*dc == *stop) { stop++; dc = delimeters; } else dc++; } // While if (*stop == '\0') st->start = NULL; else st->start = stop; } else { st->start = NULL; st->delimeter = '\0'; } } else { st->start = NULL; result = NULL; } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strlower ID:1 Purpose: Converts a string to lowercase Input: char *convertme : string to convert Output: Errors: ------------------------------------------------------------------------*/ int PLD_strlower( unsigned char *convertme ) { // Updates: // 09-11-2002 - changed from 'char *' to 'unsigned char *' to deal with // non-ASCII characters ( ie, french ). Pointed out by Emmanuel Collignon char *c = convertme; while ( *c != '\0') {*c = tolower((int)*c); c++;} return 0; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char *source, Original buffer, \0 terminated 2. char *searchfor, String sequence to search for 3. char *replacewith, String sequence to replace 'searchfor' with 4. int replacenumber , How many times to replace 'searchfor', 0 == unlimited ------------------ Exit Codes : Returns a pointer to the new buffer space. The original buffer will still remain intact - ensure that the calling program free()'s the original buffer if it's no longer needed Side Effects : -------------------------------------------------------------------- Comments: Start out with static text matching - upgrade to regex later. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace_general( struct PLD_strreplace *replace_details ) { char *new_buffer=NULL; char *source_end; char *segment_start, *segment_end, *segment_p; char *new_p; char *preexist_location=NULL; char *postexist_location=NULL; int replace_count=0; int size_required; int size_difference; int source_length; int searchfor_length; int replacewith_length; int segment_ok; if (replace_details->source == NULL) return NULL; source_length = strlen( replace_details->source ); source_end = replace_details->source +source_length; searchfor_length = strlen(replace_details->searchfor); replacewith_length = strlen(replace_details->replacewith); size_difference = replacewith_length -searchfor_length; size_required = source_length; replace_count = replace_details->replacenumber; if ((replace_details->preexist != NULL)&&(strlen(replace_details->preexist) < 1)) replace_details->preexist = NULL; if ((replace_details->postexist != NULL)&&(strlen(replace_details->postexist) < 1)) replace_details->postexist = NULL; // If we have a 'pre-exist' request, then we need to check this out first // because if the pre-exist string cannot be found, then there's very // little point us continuing on in our search ( because without the // preexist string existing, we are thus not qualified to replace anything ) if (replace_details->preexist != NULL) { preexist_location = PLD_strstr(replace_details->source, replace_details->preexist, replace_details->insensitive); if (preexist_location == NULL) { return replace_details->source; } } // Determine if initial POSTexist tests will pass, if we don't pick up // anything here, then there's no point in continuing either if (replace_details->postexist != NULL) { char *p = replace_details->source; postexist_location = NULL; do { p = PLD_strstr(p, replace_details->postexist, replace_details->insensitive); if (p != NULL) { postexist_location = p; p = p +strlen(replace_details->postexist); } } while (p != NULL); if (postexist_location == NULL) { return replace_details->source; } } // Step 1 - determine the MAXIMUM number of times we might have to replace this string ( or the limit // set by replacenumber // // Note - we only need this number if the string we're going to be inserting into the // source is larger than the one we're replacing - this is so that we can ensure that // we have sufficient memory available in the buffer. if (size_difference > 0) { if (replace_count == 0) { char *p, *q; p = replace_details->source; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); while (q != NULL) { replace_count++; //size_required += size_difference; p = q +searchfor_length; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); } } size_required = source_length +(size_difference *replace_count) +1; } else size_required = source_length +1; // Allocate the memory required to hold the new string [at least], check to see that // all went well, if not, then return an error new_buffer = malloc( sizeof(char) *size_required); if (new_buffer == NULL) { LOGGER_log("%s:%d:PLD_strreplace:ERROR: Cannot allocate %d bytes of memory to perform replacement operation", FL, size_required); return replace_details->source; } // Our segment must always start at the beginning of the source, // on the other hand, the segment_end can be anything from the // next byte to NULL ( which is specially treated to mean to // the end of the source ) segment_start = replace_details->source; // Locate the first segment segment_ok = 0; segment_end = PLD_strstr(replace_details->source, replace_details->searchfor, replace_details->insensitive); // Determine if the first segment is valid in the presence of the // pre-exist and post-exist requirements while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } segment_p = segment_start; new_p = new_buffer; while (segment_start != NULL) { int replacewith_count; char *replacewith_p; if (segment_end == NULL) segment_end = source_end; replace_count--; // Perform the segment copy segment_p = segment_start; while ((segment_p < segment_end)&&(size_required > 0)) { *new_p = *segment_p; new_p++; segment_p++; size_required--; } // Perform the string replacement if (segment_end < source_end) { replacewith_count = replacewith_length; replacewith_p = replace_details->replacewith; while ((replacewith_count--)&&(size_required > 0)) { *new_p = *replacewith_p; new_p++; replacewith_p++; size_required--; } } if (size_required < 1 ) { LOGGER_log("%s:%d:PLD_strreplace_general: Allocated memory ran out while replacing '%s' with '%s'",FL, replace_details->searchfor, replace_details->replacewith); *new_p='\0'; break; } // Find the next segment segment_start = segment_end +searchfor_length; // If we've reached the end of the number of replacements we're supposed // to do, then we prepare the termination of the while loop by setting // our segment end to the end of the source. // // NOTE: Remember that the replace_count is pre-decremented at the start // of the while loop, so, if the caller requested '0' replacements // this will now be -1, thus, it won't get terminated from this == 0 // match. Just thought you'd like to be reminded of that incase you // were wondering "Huh? this would terminate an unlimited replacement" if (replace_count == 0) { segment_end = NULL; } else { // If our new segment to copy starts after the // end of the source, then we actually have // nothing else to copy, thus, we prepare the // segment_start varible to cause the while loop // to terminate. // // Otherwise, we try and locate the next segment // ending point, and set the starting point to // be on the 'other side' of the 'searchfor' string // which we found in the last search. // if (segment_start > source_end) { segment_start = NULL; } else { // Try find the next segment segment_ok = 0; segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); // If we have a pre/post-exist requirement, then enter into this // series of tests. NOTE - at least one of the pre or post tests // must fire to give an meaningful result - else we'll end up with // a loop which simply goes to the end of the searchspace buffer while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } } // If-else segment_start > source_end } } *new_p = '\0'; if (replace_details->source != NULL) free (replace_details->source); replace_details->source = new_buffer; return new_buffer; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char **source, 2. char *searchfor, 3. char *replacewith, 4. int replacenumber , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ) { struct PLD_strreplace replace_details; char *tmp_source; replace_details.source = *source; replace_details.searchfor = searchfor; replace_details.replacewith = replacewith; replace_details.replacenumber = replacenumber; replace_details.preexist = NULL; replace_details.postexist = NULL; replace_details.insensitive = 0; tmp_source = PLD_strreplace_general( &replace_details ); if (tmp_source != *source) *source = tmp_source; return *source; } /*-----------------------------------------------------------------\ Function Name : *PLD_dprintf Returns Type : char ----Parameter List 1. const char *format, 2. ..., ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This is a dynamic string allocation function, not as fast as some other methods, but it works across the board with both glibc 2.0 and 2.1 series. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_dprintf(const char *format, ...) { int n, size = 1024; // Assume we don't need more than 1K to start with char *p; va_list ap; // Attempt to allocate and then check p = malloc(size *sizeof(char)); if (p == NULL) return NULL; while (1) { // Attempt to print out string out into the allocated space va_start(ap, format); n = vsnprintf (p, size, format, ap); va_end(ap); // If things went well, then return the new string if ((n > -1) && (n < size)) return p; // If things didn't go well, then we have to allocate more space // based on which glibc we're using ( fortunately, the return codes // tell us which glibc is being used! *phew* // // If n > -1, then we're being told precisely how much space we need // else (older glibc) we have to just guess again ... if (n > -1) size = n+1; // Allocate precisely what is needed else size *= 2; // Double the amount allocated, note, we could just increase by 1K, but if we have a long string, we'd end up using a lot of realloc's // We could just realloc 'blind', but that'd be wrong and potentially cause a DoS, so // instead, we'll be good and first attempt to realloc to a temp variable then, if all // is well, we go ahead and update if (1) { char *tmp_p; tmp_p = realloc(p, size); if (tmp_p == NULL){ if (p != NULL) free(p); return NULL; } else p = tmp_p; } } } //-----------------END. p3scan-2.3.2/ripmime-1.4.0.6/ripOLE/pldstr.h0000644000175000001440000000166510347164455016643 0ustar jlaiusers#ifndef __PLDSTR__ #define __PLDSTR__ #ifndef FL #define FL __FILE__,__LINE__ #endif struct PLD_strtok { char *start; char delimeter; }; struct PLD_strreplace { char *source; char *searchfor; char *replacewith; char *preexist; char *postexist; int replacenumber; int insensitive; }; char *PLD_strstr(char *haystack, char *needle, int insensitive); char *PLD_strncpy( char *dst, const char *src, size_t len ); char *PLD_strncat( char *dst, const char *src, size_t len ); char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ); char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ); int PLD_strncasecmp( char *s1, char *s2, int n ); int PLD_strlower( unsigned char *convertme ); char *PLD_strreplace_general( struct PLD_strreplace *replace_details ); char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ); char *PLD_dprintf(const char *fmt, ...); #endif p3scan-2.3.2/ripmime-1.4.0.6/logger.c0000644000175000001440000001705310347164530015443 0ustar jlaiusers // Abstract logging system used to facilitate multiple modes // of logging #include #include #include #ifndef WIN32 #include #endif #include #include #include #include "logger.h" #ifndef WIN32 static int _LOGGER_mode = _LOGGER_SYSLOG; static int _LOGGER_syslog_mode = LOG_MAIL|LOG_INFO; #else static int _LOGGER_mode = _LOGGER_STDERR; static int _LOGGER_syslog_mode = 0; #endif static FILE *_LOGGER_outf; struct LOGGER_globals { int wrap; int wraplength; }; // Create and Initialise the global structure for LOGGER, // we init it to have NO wrapping. static struct LOGGER_globals LOGGER_glb={ 0, 0 }; /*------------------------------------------------------------------------ Procedure: LOGGER_get_file ID:1 Purpose: Returns the pointer to the file being used to output logs to Input: Output: Errors: ------------------------------------------------------------------------*/ FILE *LOGGER_get_file( void ) { return _LOGGER_outf; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_mode ID:1 Purpose: Sets the message/log output method, ie, stderr, stdout or syslog Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_mode( int modechoice ) { _LOGGER_mode = modechoice; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_file ID:1 Purpose: Sets the output file for when _LOGGER_mode is set to _LOGGER_file Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_file( FILE *f ) { _LOGGER_outf = f; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_syslog_mode ID:1 Purpose: Sets the mode that messaging to the syslog daemon will be sent as (ie, LOG_MAIL|LOG_INFO) Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_syslog_mode( int syslogmode ) { _LOGGER_syslog_mode = syslogmode; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_logfile ID:1 Purpose: Opens and setups the internal Log file file pointer with the log file as given by lfname Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_logfile( char *lfname ) { int result = 0; _LOGGER_outf = fopen(lfname,"a"); if (!_LOGGER_outf) { #ifndef WIN32 syslog(1,"LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)",lfname,strerror(errno)); #else fprintf(stderr, "LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)\n", lfname, strerror(errno)); #endif result = -1; } return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wraplength ID:1 Purpose: Sets the character count at which LOGGER will break a line Input: int length: Positive integer indicating number of chracters at which to wrap at Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wraplength( int length ) { if ( length >= 0 ) { LOGGER_glb.wraplength = length; } return LOGGER_glb.wraplength; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wrap ID:1 Purpose: Set log output wrapping to on or off Input: int level: 0 = no wrap, > 0 = wrap. Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wrap( int level ) { if ( level >= 0 ) { LOGGER_glb.wrap = level; } return LOGGER_glb.wrap; } /*------------------------------------------------------------------------ Procedure: LOGGER_close_logfile ID:1 Purpose: Closes the modules log file pointer. Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_close_logfile( void ) { int result = 0; if (_LOGGER_outf) fclose(_LOGGER_outf); return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_clean_output ID:1 Purpose: Checks through the output string for any characters which could cause potential 'isssues' with the data writing calls, items such as stray non-escaped % characters can cause havoc. Input: char *string: Raw string int maxsize: Maximum available buffer size for this string to expand to Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_clean_output( char *string, char **buffer ) { char *newstr; char *p, *q; char *next_space; int pc; int slen = strlen( string ); int line_size; int maxsize = slen *2; // First up, allocate maxsize bytes for a temporary new string. newstr = malloc(slen *2 +1); if ( newstr == NULL ) { // FIXME - Report an error here ... to -somewhere- return -1; } p = newstr; q = string; pc = 0; line_size = 0; while (slen--) { // Do we need to apply any wrapping to the output? If so then we // shall embark on a journey of strange space and distance // evaluations to determine if we should wrap now or later if ( LOGGER_glb.wrap > 0 ) { if (isspace((int)*q)) { next_space = strpbrk( (q+1), "\t\r\n\v " ); if (next_space != NULL) { if ((line_size +(next_space -q)) >= LOGGER_glb.wraplength) { *p = '\n'; p++; pc++; line_size = 0; } } } if ( line_size >= LOGGER_glb.wraplength ) { *p = '\n'; p++; pc++; line_size = 0; } } // If the string has a % in it, then we need to encode it as // a DOUBLE % symbol. if (*q == '%') { // if (strchr("fdlsxXn",*(q+1))) // { *p = '%'; p++; pc++; // } } // Copy the character of the string in *p = *q; // Move everything along. q++; p++; pc++; line_size++; if ( pc > (maxsize -1) ) { break; } } *p = '\0'; // This will have to be deallocated later! if (newstr) *buffer = newstr; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_log ID:1 Purpose: Logs the params as supplied to the required output as defined by LOGGER_set_output Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_log( char *format, ...) { va_list ptr; char tmpoutput[10240]; char linebreak[]="\n"; char nolinebreak[]=""; char *lineend; char *output; // get our variable arguments va_start(ptr,format); // produce output, and spit to the log file #ifdef NO_SNPRINTF vsprintf(tmpoutput, format, ptr); #else vsnprintf(tmpoutput,10240,format,ptr); #endif LOGGER_clean_output( tmpoutput, &output ); if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } // Send the output to the appropriate output destination switch (_LOGGER_mode) { case _LOGGER_STDERR: fprintf(stderr,"%s%s",output, lineend ); break; case _LOGGER_SYSLOG: syslog(_LOGGER_syslog_mode,output); break; case _LOGGER_STDOUT: fprintf(stdout,"%s%s",output, lineend); fflush(stdout); break; case _LOGGER_FILE: fprintf(_LOGGER_outf,"%s%s",output,lineend); fflush(_LOGGER_outf); break; default: fprintf(stdout,"LOGGER-Default: %s%s",output,lineend); } if (output) free(output); return 0; } p3scan-2.3.2/ripmime-1.4.0.6/logger.h0000644000175000001440000000110510347164530015437 0ustar jlaiusers #ifndef __LOGGER__ #define __LOGGER__ // LOGGER.h // #define _LOGGER_STDERR 1 #define _LOGGER_STDOUT 2 #define _LOGGER_FILE 3 #ifndef WIN32 #define _LOGGER_SYSLOG 4 #endif #ifndef FL #define FL __FILE__,__LINE__ #endif int LOGGER_log( char *format, ...); int LOGGER_set_output_mode( int modechoice ); int LOGGER_set_output_file( FILE *f ); int LOGGER_set_syslog_mode( int syslogmode ); int LOGGER_set_logfile( char *lfname ); int LOGGER_set_wraplength( int length ); int LOGGER_set_wrap( int level ); int LOGGER_close_logfile( void ); FILE *LOGGER_get_file( void ); #endif p3scan-2.3.2/ripmime-1.4.0.6/INSTALL0000644000175000001440000000152110347164530015042 0ustar jlaiusersINSTALL--------------------------------------------------------------- Very simple (as root)... make make install [ Make install will install the ripmime binary to /usr/local/bin, there are no other support files required. ] To use ripMIME... If you have an MIME encoded archive (say from sendmail's output) called recvmail001.mime and you wish to unpack it to a directory (which already exists) called "/var/tmp/unpack", then use ripMIME as follows... ripmime -i recvmail001.mime -d /var/tmp/unpack It's truly that simple. --- If you want more PERFORMANCE out of ripMIME (most people will), you can select some compile flags in the Makefile which suit your CPU better, ie, for recent Pentiums/Celerons, you can use -Wall -O3 -march=i686 ---------------------------------------------------------------------- Enjoy. p3scan-2.3.2/ripmime-1.4.0.6/pldstr.c0000644000175000001440000005154010347164530015473 0ustar jlaiusers #include #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" /*-----------------------------------------------------------------\ Function Name : *PLD_strstr Returns Type : char ----Parameter List 1. char *haystack, 2. char *needle, 3. int insensitive, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strstr(char *haystack, char *needle, int insensitive) { char *hs, *ne; char *result; // LOGGER_log("%s:%d:\nHS=%s\nNE=%s\nIS=%d\n",FL, haystack, needle, insensitive ); if (insensitive > 0) { hs = strdup(haystack); PLD_strlower(hs); ne = strdup(needle); PLD_strlower(ne); } else { hs = haystack; ne = needle; } result = strstr(hs, ne); // if (result) LOGGER_log("%s:%d:HIT: %s",FL, result); // else LOGGER_log("%s:%d:MISS (looking for %s|%s)",FL, needle,ne); if ((result != NULL)&&(insensitive > 0)) { result = result -hs +haystack; free(hs); free(ne); // LOGGER_log("%s:%d:HIT - %s",FL, result ); } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strncpy ID:1 Purpose: Copy characters from 'src' to 'dst', writing not more than 'len' characters to the destination, including the terminating \0. Thus, for any effective copying, len must be > 1. Input: char *dst: Destination string char *src: Source string size_t len: length of string Output: Returns a pointer to the destination string. Errors: ------------------------------------------------------------------------*/ char *PLD_strncpy (char *dst, const char *src, size_t len) { // Thanks go to 'defrost' of #c for providing the replacement // code which you now see here. It covers the errors better // than my own previous code. // If we have no buffer space, then it's futile attempting // to copy anything, just return NULL if (len==0) return NULL; // Providing our destination pointer isn't NULL, we can // commence copying data across if (dst) { char *dp = dst; // If our source string exists, start moving it to the // destination string character at a time. if (src) { char *sp = (char *)src; while ((--len)&&(*sp)) { *dp=*sp; dp++; sp++; } } *dp='\0'; } return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncat ID:1 Purpose: Buffer size limited string concat function for two strings. Input: char *dst: Destination string char *src: Source string size_t len: Destination string buffer size - total string size cannot exceed this Output: Errors: If the length of both strings in total is greater than the available buffer space in *dst, we copy the maximum possible amount of chars from *src such that buffer does not overflow. A suffixed '\0' will always be appended. ------------------------------------------------------------------------*/ char *PLD_strncat( char *dst, const char *src, size_t len ) { char *dp = dst; const char *sp = src; int cc; if (len == 0) return dst; len--; // Locate the end of the current string. cc = 0; while ((*dp)&&(cc < len)) { dp++; cc++; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncate ID:1 Purpose: Catencates a source string to the destination string starting from a given endpoint. This allows for faster catencation of strings by avoiding the computation required to locate the endpoint of the destination string. Input: char *dst: Destination string char *src: Source string size_t len: Destination buffer size char *endpoint: Endpoint of destination string, location from where new string will be appended Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ) { char *dp = dst; const char *sp = src; int cc = 0; if (len == 0) return dst; len--; // If endpoint does not relate correctly, then force manual detection // of the endpoint. if ((!endpoint)||(endpoint == dst)||((endpoint -dst +1)>len)) { // Locate the end of the current string. cc = 0; while ((*dp != '\0')&&(cc < len)) { dp++; cc++; } } else { cc = endpoint -dst +1; dp = endpoint; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: XAM_strncasecmp ID:1 Purpose: Portable version of strncasecmp(), this may be removed in later versions as the strncase* type functions are more widely implemented Input: Output: Errors: ------------------------------------------------------------------------*/ int PLD_strncasecmp( char *s1, char *s2, int n ) { char *ds1 = s1, *ds2 = s2; char c1, c2; int result = 0; while(n > 0) { c1 = tolower(*ds1); c2 = tolower(*ds2); if (c1 == c2) { n--; ds1++; ds2++; } else { result = c2 - c1; n = 0; } } return result; } /*------------------------------------------------------------------------ Procedure: XAM_strtok ID:1 Purpose: A thread safe version of strtok() Input: Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ) { char *stop; char *dc; char *result = NULL; if ( line ) { st->start = line; } //Strip off any leading delimeters dc = delimeters; while ((st->start)&&(*dc != '\0')) { if (*dc == *(st->start)) { st->start++; dc = delimeters; } else dc++; } // Where we are left, is the start of our token. result = st->start; if ((st->start)&&(st->start != '\0')) { stop = strpbrk( st->start, delimeters ); /* locate our next delimeter */ // If we found a delimeter, then that is good. We must now break the string here // and don't forget to store the character which we stopped on. Very useful bit // of information for programs which process expressions. if (stop) { // Store our delimeter. st->delimeter = *stop; // Terminate our token. *stop = '\0'; // Because we're emulating strtok() behaviour here, we have to // absorb all the concurrent delimeters, that is, unless we // reach the end of the string, we cannot return a string with // no chars. stop++; dc = delimeters; while (*dc != '\0') { if (*dc == *stop) { stop++; dc = delimeters; } else dc++; } // While if (*stop == '\0') st->start = NULL; else st->start = stop; } else { st->start = NULL; st->delimeter = '\0'; } } else { st->start = NULL; result = NULL; } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strlower ID:1 Purpose: Converts a string to lowercase Input: char *convertme : string to convert Output: Errors: ------------------------------------------------------------------------*/ int PLD_strlower( unsigned char *convertme ) { // Updates: // 09-11-2002 - changed from 'char *' to 'unsigned char *' to deal with // non-ASCII characters ( ie, french ). Pointed out by Emmanuel Collignon unsigned char *c = convertme; while ( *c != '\0') {*c = tolower(*c); c++;} return 0; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char *source, Original buffer, \0 terminated 2. char *searchfor, String sequence to search for 3. char *replacewith, String sequence to replace 'searchfor' with 4. int replacenumber , How many times to replace 'searchfor', 0 == unlimited ------------------ Exit Codes : Returns a pointer to the new buffer space. The original buffer will still remain intact - ensure that the calling program free()'s the original buffer if it's no longer needed Side Effects : -------------------------------------------------------------------- Comments: Start out with static text matching - upgrade to regex later. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace_general( struct PLD_strreplace *replace_details ) { char *new_buffer=NULL; char *source_end; char *segment_start, *segment_end, *segment_p; char *new_p; char *preexist_location=NULL; char *postexist_location=NULL; int replace_count=0; int size_required; int size_difference; int source_length; int searchfor_length; int replacewith_length; int segment_ok; if (replace_details->source == NULL) return NULL; source_length = strlen( replace_details->source ); source_end = replace_details->source +source_length; searchfor_length = strlen(replace_details->searchfor); replacewith_length = strlen(replace_details->replacewith); size_difference = replacewith_length -searchfor_length; size_required = source_length; replace_count = replace_details->replacenumber; if ((replace_details->preexist != NULL)&&(strlen(replace_details->preexist) < 1)) replace_details->preexist = NULL; if ((replace_details->postexist != NULL)&&(strlen(replace_details->postexist) < 1)) replace_details->postexist = NULL; // If we have a 'pre-exist' request, then we need to check this out first // because if the pre-exist string cannot be found, then there's very // little point us continuing on in our search ( because without the // preexist string existing, we are thus not qualified to replace anything ) if (replace_details->preexist != NULL) { preexist_location = PLD_strstr(replace_details->source, replace_details->preexist, replace_details->insensitive); if (preexist_location == NULL) { return replace_details->source; } } // Determine if initial POSTexist tests will pass, if we don't pick up // anything here, then there's no point in continuing either if (replace_details->postexist != NULL) { char *p = replace_details->source; postexist_location = NULL; do { p = PLD_strstr(p, replace_details->postexist, replace_details->insensitive); if (p != NULL) { postexist_location = p; p = p +strlen(replace_details->postexist); } } while (p != NULL); if (postexist_location == NULL) { return replace_details->source; } } // Step 1 - determine the MAXIMUM number of times we might have to replace this string ( or the limit // set by replacenumber // // Note - we only need this number if the string we're going to be inserting into the // source is larger than the one we're replacing - this is so that we can ensure that // we have sufficient memory available in the buffer. if (size_difference > 0) { if (replace_count == 0) { char *p, *q; p = replace_details->source; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); while (q != NULL) { replace_count++; //size_required += size_difference; p = q +searchfor_length; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); } } size_required = source_length +(size_difference *replace_count) +1; } else size_required = source_length +1; // Allocate the memory required to hold the new string [at least], check to see that // all went well, if not, then return an error new_buffer = malloc( sizeof(char) *size_required); if (new_buffer == NULL) { LOGGER_log("%s:%d:PLD_strreplace:ERROR: Cannot allocate %d bytes of memory to perform replacement operation", FL, size_required); return replace_details->source; } // Our segment must always start at the beginning of the source, // on the other hand, the segment_end can be anything from the // next byte to NULL ( which is specially treated to mean to // the end of the source ) segment_start = replace_details->source; // Locate the first segment segment_ok = 0; segment_end = PLD_strstr(replace_details->source, replace_details->searchfor, replace_details->insensitive); // Determine if the first segment is valid in the presence of the // pre-exist and post-exist requirements while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } segment_p = segment_start; new_p = new_buffer; while (segment_start != NULL) { int replacewith_count; char *replacewith_p; if (segment_end == NULL) segment_end = source_end; replace_count--; // Perform the segment copy segment_p = segment_start; while ((segment_p < segment_end)&&(size_required > 0)) { *new_p = *segment_p; new_p++; segment_p++; size_required--; } // Perform the string replacement if (segment_end < source_end) { replacewith_count = replacewith_length; replacewith_p = replace_details->replacewith; while ((replacewith_count--)&&(size_required > 0)) { *new_p = *replacewith_p; new_p++; replacewith_p++; size_required--; } } if (size_required < 1 ) { LOGGER_log("%s:%d:PLD_strreplace_general: Allocated memory ran out while replacing '%s' with '%s'",FL, replace_details->searchfor, replace_details->replacewith); *new_p='\0'; break; } // Find the next segment segment_start = segment_end +searchfor_length; // If we've reached the end of the number of replacements we're supposed // to do, then we prepare the termination of the while loop by setting // our segment end to the end of the source. // // NOTE: Remember that the replace_count is pre-decremented at the start // of the while loop, so, if the caller requested '0' replacements // this will now be -1, thus, it won't get terminated from this == 0 // match. Just thought you'd like to be reminded of that incase you // were wondering "Huh? this would terminate an unlimited replacement" if (replace_count == 0) { segment_end = NULL; } else { // If our new segment to copy starts after the // end of the source, then we actually have // nothing else to copy, thus, we prepare the // segment_start varible to cause the while loop // to terminate. // // Otherwise, we try and locate the next segment // ending point, and set the starting point to // be on the 'other side' of the 'searchfor' string // which we found in the last search. // if (segment_start > source_end) { segment_start = NULL; } else { // Try find the next segment segment_ok = 0; segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); // If we have a pre/post-exist requirement, then enter into this // series of tests. NOTE - at least one of the pre or post tests // must fire to give an meaningful result - else we'll end up with // a loop which simply goes to the end of the searchspace buffer while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } } // If-else segment_start > source_end } } *new_p = '\0'; if (replace_details->source != NULL) free (replace_details->source); replace_details->source = new_buffer; return new_buffer; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char **source, 2. char *searchfor, 3. char *replacewith, 4. int replacenumber , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ) { struct PLD_strreplace replace_details; char *tmp_source; replace_details.source = *source; replace_details.searchfor = searchfor; replace_details.replacewith = replacewith; replace_details.replacenumber = replacenumber; replace_details.preexist = NULL; replace_details.postexist = NULL; replace_details.insensitive = 0; tmp_source = PLD_strreplace_general( &replace_details ); if (tmp_source != *source) *source = tmp_source; return *source; } /*-----------------------------------------------------------------\ Function Name : *PLD_dprintf Returns Type : char ----Parameter List 1. const char *format, 2. ..., ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This is a dynamic string allocation function, not as fast as some other methods, but it works across the board with both glibc 2.0 and 2.1 series. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_dprintf(const char *format, ...) { int n, size = 1024; // Assume we don't need more than 1K to start with char *p; va_list ap; // Attempt to allocate and then check p = malloc(size *sizeof(char)); if (p == NULL) return NULL; while (1) { // Attempt to print out string out into the allocated space va_start(ap, format); n = vsnprintf (p, size, format, ap); va_end(ap); // If things went well, then return the new string if ((n > -1) && (n < size)) return p; // If things didn't go well, then we have to allocate more space // based on which glibc we're using ( fortunately, the return codes // tell us which glibc is being used! *phew* // // If n > -1, then we're being told precisely how much space we need // else (older glibc) we have to just guess again ... if (n > -1) size = n+1; // Allocate precisely what is needed else size *= 2; // Double the amount allocated, note, we could just increase by 1K, but if we have a long string, we'd end up using a lot of realloc's // We could just realloc 'blind', but that'd be wrong and potentially cause a DoS, so // instead, we'll be good and first attempt to realloc to a temp variable then, if all // is well, we go ahead and update if (1) { char *tmp_p; tmp_p = realloc(p, size); if (tmp_p == NULL){ if (p != NULL) free(p); return NULL; } else p = tmp_p; } } } //-----------------END. p3scan-2.3.2/ripmime-1.4.0.6/pldstr.h0000644000175000001440000000166510347164530015503 0ustar jlaiusers#ifndef __PLDSTR__ #define __PLDSTR__ #ifndef FL #define FL __FILE__,__LINE__ #endif struct PLD_strtok { char *start; char delimeter; }; struct PLD_strreplace { char *source; char *searchfor; char *replacewith; char *preexist; char *postexist; int replacenumber; int insensitive; }; char *PLD_strstr(char *haystack, char *needle, int insensitive); char *PLD_strncpy( char *dst, const char *src, size_t len ); char *PLD_strncat( char *dst, const char *src, size_t len ); char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ); char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ); int PLD_strncasecmp( char *s1, char *s2, int n ); int PLD_strlower( unsigned char *convertme ); char *PLD_strreplace_general( struct PLD_strreplace *replace_details ); char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ); char *PLD_dprintf(const char *fmt, ...); #endif p3scan-2.3.2/ripmime-1.4.0.6/ripmime-api.c0000755000175000001440000000632510347164530016400 0ustar jlaiusers/*---------------------------------------- * ripmime-api * * Written by Paul L Daniels * pldaniels@pldaniels.com * * (C)2001 P.L.Daniels * http://www.pldaniels.com/ripmime * */ #include #include #include #include #include #include #include #include #include #include #include "logger.h" #include "ffget.h" #include "strstack.h" #include "mime.h" #include "MIME_headers.h" #include "ripmime-api.h" char defaultdir[] = "."; char version[] = "v1.4.0.1 - 30/08/2004 (C) PLDaniels http://www.pldaniels.com/ripmime"; /*-----------------------------------------------------------------\ Function Name : RIPMIME_init Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_init (struct RIPMIME_object *rm) { rm->outputdir = defaultdir; rm->mailpack = NULL; LOGGER_set_output_mode(_LOGGER_STDOUT); MIME_init(); MIME_set_uniquenames(1); MIME_set_paranoid(0); MIME_set_renamemethod(_MIME_RENAME_METHOD_INFIX); MIME_set_verbosity(0); return 0; } /*-----------------------------------------------------------------\ Function Name : main Returns Type : int ----Parameter List 1. int argc, 2. char **argv, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_decode( struct RIPMIME_object *rm, char *mailpack, char *outputdir ) { int result = 0; if (!mailpack) { LOGGER_log("%s:%d:RIPMIME_decode: mailpack filename is NULL\n",FL); return 1; } else { rm->mailpack = strdup(mailpack); } if (!outputdir) { LOGGER_log("%s:%d:RIPMIME_decode: output directory is NULL\n",FL); return 1; } else { rm->outputdir = strdup(outputdir); } // Fire up the randomizer srand (time (NULL)); // clean up the output directory name if required (remove any trailing /'s, as suggested by James Cownie 03/02/2001 if (rm->outputdir[strlen (rm->outputdir) - 1] == '/') { rm->outputdir[strlen (rm->outputdir) - 1] = '\0'; } // Create the output directory required as specified by the -d parameter if (rm->outputdir != defaultdir) { result = mkdir (rm->outputdir, S_IRWXU); // if we had a problem creating a directory, and it wasn't just // due to the directory already existing, then we have a bit of // a problem on our hands, hence, report it. // if ((result == -1) && (errno != EEXIST)) { fprintf (stderr, "ripMIME: Cannot create directory '%s' (%s)\n", rm->outputdir, strerror (errno)); return -1; } } // Unpack the contents MIMEH_set_outputdir(rm->outputdir); MIME_unpack (rm->outputdir, rm->mailpack, 0); // do any last minute things MIME_close (); return 0; } /*-END-----------------------------------------------------------*/ p3scan-2.3.2/ripmime-1.4.0.6/ripmime-api.h0000644000175000001440000000030210347164530016367 0ustar jlaiusers struct RIPMIME_object { char *mailpack; char *outputdir; }; int RIPMIME_init( struct RIPMIME_object *rm ); int RIPMIME_decode( struct RIPMIME_object *rm, char *mailpack, char *outputdir ); p3scan-2.3.2/ripmime-1.4.0.6/strstack.c0000644000175000001440000001536210347164530016023 0ustar jlaiusers#include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "strstack.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define DSS if (ss->debug) /*-----------------------------------------------------------------\ Function Name : SS_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_init( struct SS_object *ss ) { ss->debug = 0; ss->verbose = 0; ss->count = 0; ss->stringstack = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : SS_set_verbose Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_set_verbose( struct SS_object *ss, int level ) { ss->verbose = level; return ss->verbose; } /*-----------------------------------------------------------------\ Function Name : SS_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_set_debug( struct SS_object *ss, int level ) { ss->debug = level; return ss->debug; } /*-----------------------------------------------------------------\ Function Name : SS_done Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_done( struct SS_object *ss ) { struct SS_node *next; while ((ss->stringstack != NULL)&&(ss->count > 0)) { DSS LOGGER_log("%s:%d:SS_done: Popping off %s",FL,ss->stringstack->data); next = ss->stringstack->next; free(ss->stringstack->data); free(ss->stringstack); ss->stringstack = next; ss->count--; } ss->stringstack = NULL; ss->count = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : SS_dump Returns Type : int ----Parameter List 1. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_dump( struct SS_object *ss ) { struct SS_node *n = ss->stringstack; while (n != NULL) { LOGGER_log("%s",n->data); n = n->next; } return 0; } /*-----------------------------------------------------------------\ Function Name : SS_push Returns Type : int ----Parameter List 1. char *string , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_push( struct SS_object *ss, char *data, size_t data_length ) { struct SS_node *node = malloc(sizeof(struct SS_node)); if (node) { DSS LOGGER_log("%s:%d:SS_push: Pushing %s to %p, stack count = %d",FL,data, ss->stringstack, ss->count); node->next = ss->stringstack; ss->stringstack = node; ss->stringstack->data = strdup(data); ss->stringstack->data_length = data_length; ss->count++; } else { LOGGER_log("%s:%d:SS_push:ERROR: Cannot allocate memory for string stack PUSH, %s", FL, strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : *SS_pop Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_pop( struct SS_object *ss ) { struct SS_node *node = ss->stringstack; if ((ss->stringstack)&&(ss->count > 0)) { ss->stringstack = ss->stringstack->next; PLD_strncpy(ss->datastacksafe,node->data, SS_STRLEN_MAX); free(node->data); free(node); ss->count--; } else return NULL; return ss->datastacksafe; } /*-----------------------------------------------------------------\ Function Name : *SS_top Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_top( struct SS_object *ss ) { if (ss->stringstack) { return ss->stringstack->data; } else return NULL; } /*-----------------------------------------------------------------\ Function Name : SS_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_count( struct SS_object *ss ) { return ss->count; } /*-----------------------------------------------------------------\ Function Name : *SS_cmp Returns Type : char ----Parameter List 1. struct SS_object *ss, 2. char *find_me , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_cmp( struct SS_object *ss, char *find_me, size_t find_me_len ) { struct SS_node *n = ss->stringstack; int hit=0; while ((n != NULL)&&(hit == 0)) { if (strncmp(find_me, n->data, find_me_len) == 0) hit++; if (hit == 0) n = n->next; } if (hit == 0) return NULL; else return n->data; } p3scan-2.3.2/ripmime-1.4.0.6/strstack.h0000644000175000001440000000141710347164530016024 0ustar jlaiusers #ifndef __STRSTACK__ #define __STRSTACK__ #define SS_STRLEN_MAX 1024 struct SS_node { char *data; size_t data_length; struct SS_node *next; }; struct SS_object { int debug; int verbose; int count; int detect_limit; struct SS_node *stringstack; char datastacksafe[SS_STRLEN_MAX]; }; int SS_init( struct SS_object *ss ); int SS_set_verbose( struct SS_object *ss, int level ); int SS_set_debug( struct SS_object *ss, int level ); int SS_push( struct SS_object *ss, char *data, size_t data_length ); char *SS_pop( struct SS_object *ss ); char *SS_top( struct SS_object *ss ); char *SS_cmp( struct SS_object *ss, char *find_me, size_t find_me_len ); int SS_dump( struct SS_object *ss ); int SS_count( struct SS_object *ss ); int SS_done( struct SS_object *ss ); #endif p3scan-2.3.2/CHANGELOG0000644000175000001440000000126610347310635012764 0ustar jlaiusersChanges since release 2.3.1 Update RipMIME to 1.4.0.6 Enable p3scan abort on emergency error. Wednesday, November 30th, 2005 Fixed problem with demime introduced in 2.3.0. (Thank you James "Kimo" Lay for reporting it) Intercept "250-PIPELINING" for SMTP. Fixed p3scan.extra reporting problem. (Thank you Maxim Britov for reporting it) Force writing "\r\n" during header flush on virus action. (Thank you "quizac" for reporting it) Changed license to reflect OpenSSL use. (http://www.openssl.org/support/faq.html#LEGAL2) ((Thank you Mats Rynge for bringing it to my attention)) Tuesday, November 25th, 2005 Fixed possible hang when using "useurl" feature. p3scan-2.3.2/p3scan-sp.mail0000644000175000001440000000145710347310635014227 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Hola %USERNAME%. El texto de este mensaje ha sido generado automticamente por P3Scan, que corre en %HOSTNAME%.%DOMAINNAME% para limpiar de virus los mails entrantes. Se ha reemplazado el cuerpo de un mensaje que contena un VIRUS. En lugar del mensaje infectado, se enva este aviso. La cabecera de este mensaje contiene la informacion original del mensaje infectado. Nombre del virus: %VIRUSNAME% Enviado por: %MAILFROM% Enviado a: %MAILTO% Fecha: %MAILDATE% Asunto: %SUBJECT% Datos de la conexin: %PROTOCOL% from %CLIENTIP%:%CLIENTPORT% to %SERVERIP%:%SERVERPORT% Archivo del mensaje: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% by Jack S. Lai . p3scan-2.3.2/scanner.h0000644000175000001440000000361010347310635013347 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #ifndef _SCANNER_H #define _SCANNER_H #include "p3scan.h" extern scanner_t scanner_basic; extern scanner_t scanner_avpd; extern scanner_t scanner_avpd_new; extern scanner_t scanner_trophie; extern scanner_t scanner_clamd; extern scanner_t scanner_bash; scanner_t *scannerlist[] = { &scanner_basic, /* first one is default */ &scanner_avpd, &scanner_avpd_new, &scanner_trophie, &scanner_clamd, &scanner_bash, NULL }; #endif p3scan-2.3.2/scanner_avpd_new.c0000644000175000001440000003420710347310635015233 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "p3scan.h" #define DEFAULT_SOCKET_PATH "/var/run" struct configuration_t * config; extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); typedef unsigned long ULONG; #define FL_GETVERSION 0x04 // ... Cfg pathnames //#define AVP_NODE_DEFDIR "/var/run" typedef struct pid_ctl_st { char *pid; char *ctl; int version; } pid_ctl; static pid_ctl kav_run [] = { {"AvpPid", "AvpCtl", 4}, {"aveserver.pid", "aveserver", 5}, { NULL, NULL, 0 } }; int kav_version = 0; static char *NodePid = NULL; static char *NodeCtl = NULL; static int avp_fd; // fd for log static int connected; // have done connect static struct sockaddr_un avp_socket; // AF_UNIX address of local logger static int avp_socket_connect(struct proxycontext *p){ if (avp_fd == -1){ bzero((char *)&avp_socket, sizeof(avp_socket)); avp_socket.sun_family=AF_UNIX; strcpy(avp_socket.sun_path, NodeCtl); if ((avp_fd=socket(AF_UNIX,SOCK_STREAM,0)) < 0 ){ do_log(LOG_CRIT, "create socket error: socket() not created %s", NodeCtl); return -1; } } if (avp_fd!=-1 && connected==-1){ do_log(LOG_DEBUG, "Trying to connect to socket"); if (connect(avp_fd, (struct sockaddr *)(&avp_socket),sizeof(avp_socket.sun_family) + strlen(NodeCtl)) >= 0){ int ret; if (kav_version==5) { fd_set fds; struct timeval timeout; do_log(LOG_DEBUG, "avp_socket_connect fcntling"); ret = fcntl(avp_fd, F_SETFL, O_NONBLOCK); do_log(LOG_DEBUG, "avp_socket_connect fcntl=%d",ret); timeout.tv_sec = 1; timeout.tv_usec = 0; FD_ZERO(&fds); FD_SET(avp_fd, &fds); do_log(LOG_DEBUG, "avp_socket_connect selecting"); ret = select(avp_fd+1,&fds,NULL,NULL,&timeout); do_log(LOG_DEBUG, "avp_socket_connect select=%d",ret); if (ret) { char Myres[1024]; if ((ret=read(avp_fd,Myres,sizeof(Myres)))==-1) return -2; do_log(LOG_DEBUG, "Test returned: %d", ret); if (ret>0) do_log(LOG_DEBUG, "Test returned value: %s", Myres); } } connected=1; do_log(LOG_DEBUG, "avp_socket_connect connected to kavdaemon"); return 0; } } else { do_log(LOG_DEBUG, "Already connected"); return 0; } do_log(LOG_CRIT, "can't connect to socket %s", NodeCtl); return -1; } static void avp_socket_close(void){ close(avp_fd); avp_fd=-1; connected=0; do_log(LOG_DEBUG, "avp_socket_close"); } /* avp_sendcommand * return codes: * >=0: OK, avpd returncode * -1: write error * -2: read error * -3: error */ static int avp_sendcommand(struct proxycontext * p,int flags, char *buftoscan, ULONG *ulFlags, ULONG* buflen, char ** virinfo){ register int len=strlen(buftoscan); char *ResultBuf=NULL; // output the message to the local logger do_log(LOG_DEBUG, "write string (%s) to kavdaemon", buftoscan); *virinfo=NULL; if (write(avp_fd, buftoscan, len+1)>=0){ int Rez; long uintbuf=0; char Myres[4096]; int ExitCode; do_log(LOG_DEBUG, "Wait results:"); if (kav_version==5) { char *ptr,*ptr1; ResultBuf=NULL; while (1) { if ((Rez=read(avp_fd,Myres,sizeof(Myres)-1))==-1) { if (errno==EAGAIN) continue; do_log(LOG_DEBUG, "Test returned: %d %s", errno,strerror(errno)); return -2; } do_log(LOG_DEBUG, "Test returned: %d", Rez); Myres[Rez]='\0'; if (Rez>0) do_log(LOG_DEBUG, "Test returned value: %s", Myres); ptr = Myres; while ((ptr-Myres)", ptr); // Code for virus found if (strlen(ptr)>2 && ptr[2]=='2') { do_log(LOG_DEBUG, "Virus found"); //--ResultBuf=(char*)malloc(strlen(ptr)-2); ResultBuf=(char*)w_malloc(strlen(ptr)-2); strcpy(ResultBuf,ptr+4); *virinfo=ResultBuf; return 0x004; } // Final message: XXX mesg if (ptr[3]==' ') { do_log(LOG_DEBUG, "End of message"); return 0x000; } if (ptr1==NULL) break; ptr = ptr1+1; } } return 0x000; } else { if ((Rez=read(avp_fd,(char*)&uintbuf,2))==-1) return -2; ExitCode=(uintbuf&0xff)-0x30; if ((uintbuf&0x000f)!=0xf) //0x3f '?' do_log(LOG_DEBUG, "Test result: %x", ExitCode); else do_log(LOG_DEBUG,"Disinfect queries:"); do_log(LOG_DEBUG, "Test result: 0x%x, flags: 0x%x", uintbuf & 0x00ff, uintbuf & 0xff00 ); ResultBuf=NULL; if ((uintbuf&0xff00)!=0){ /* further actions */ if ((uintbuf&0x200)!=0){ /* where disinfected file is, uninteresting for us */ if ((Rez=read(avp_fd, (char*)buflen, sizeof(ULONG)))==-1) return -2; *ulFlags|=1; } if ((uintbuf&0x100)!=0){ /* we got result string to read */ if ((Rez=read(avp_fd,(char*)&uintbuf,sizeof(ULONG)))==-1) return -2; do_log(LOG_DEBUG, "Result string lenght: %d", uintbuf); //--ResultBuf=(char*)malloc(uintbuf+1); ResultBuf=(char*)w_malloc(uintbuf+1); if(ResultBuf!=NULL){ char *ResultStr=ResultBuf; ResultBuf[0]=0; //if((Rez=recv(avp_fd,ResultStr,uintbuf,0))==-1) return -2; while((uintbuf>0)&&((Rez=recv(avp_fd,ResultStr,uintbuf,0))!=0)){ if(Rez==-2){ //--free(ResultBuf); w_free(ResultBuf); return -2; } else { uintbuf-=Rez; ResultStr[Rez]=0; ResultStr+=Rez; } } } } } switch (ExitCode&0x0f){ case 8: do_log(LOG_WARNING, "Corrupted objects were found"); break; case 7: do_log(LOG_WARNING, "File AvpScanner is corrupted"); break; case 6: do_log(LOG_WARNING, "All viruses deleted"); break; case 5: do_log(LOG_WARNING, "All viruses disinfected"); break; case 4: do_log(LOG_WARNING, "Known viruses were detected"); break; case 3: do_log(LOG_WARNING, "Suspicious objects were found"); break; case 2: do_log(LOG_WARNING, "Warning"); break; case 1: do_log(LOG_WARNING, "Virus scan was not complete"); break; case 0: do_log(LOG_DEBUG, "No viruses were found"); break; case 0xf: { do_log(LOG_CRIT, "AVPD want's to disinfect! Please tell him not to do."); //--free(ResultBuf); w_free(ResultBuf); return -3; } default: do_log(LOG_WARNING, "Error!(test result %d)", Rez); break; } /* switch ExitCode */ switch (ExitCode&0xf0){ case 8: do_log(LOG_CRIT, "Internal error: Integrity failed."); break; case 4: do_log(LOG_CRIT, "Internal error: Bases not found."); break; } do_log(LOG_DEBUG, "Found viruses: '%s'", ResultBuf); //if (ResultBuf!=NULL) free(ResultBuf); *virinfo=ResultBuf; return ExitCode; } } /* if write */ return -1; } static int avp_scanfile(struct proxycontext * p, int flags, char * filetoscan, char ** virname){ int rez=-1; char *tbuf; time_t now; int len; ULONG ulFlags=0,ulDiffer=0; char *v, *v2, *virinfo; if (avp_fd<0 || !connected) if (avp_socket_connect(p)!=0) return SCANNER_RET_ERR; // build the message len=strlen(filetoscan)+30; //--tbuf=malloc(len+1); tbuf=w_malloc(len+1); (void)time(&now); if (kav_version!=5) (void)snprintf(tbuf, len, "<%d>%.15s:%s", flags, ctime(&now)+4, filetoscan); else (void)snprintf(tbuf, len, "SCAN xmQPRSTUWabcdefghi %s\r\n", filetoscan); rez=avp_sendcommand(p, flags, tbuf, &ulFlags, &ulDiffer, &virinfo); //do_log(LOG_DEBUG, "Virinfo: '%s'", virinfo); switch (rez){ case -1: do_log(LOG_CRIT, "Error: cannot write to kavdaemon!"); break; case -2: do_log(LOG_CRIT, "Error: cannot read from kavdaemon!"); break; case -3: do_log(LOG_CRIT, "Error occured during avpd conversation"); break; } //--free(tbuf); w_free(tbuf); if (virinfo){ /* process virinfo */ /* format is: infected: EICAR-Test-File */ v=virinfo; /* strip trailing filename */ if (!strncmp(v, filetoscan, strlen(filetoscan))) v+=strlen(filetoscan); /* strip trailing blanks */ while (v[0] && isspace(v[0])) v++; /* strip trailing '[a-z]*:' (if any) */ v2=v; while (v2[0] && isalnum(v2[0])) v2++; if (v2[0]==':') v=v2+1; /* strip trailing blanks */ while (v[0] && isspace(v[0])) v++; /* strip leading blanks */ while ((len=strlen(v))>0 && isspace(v[len-1])) v[len-1]='\0'; do_log(LOG_DEBUG, "virinfo: '%s'", v); *virname=strdup(v); //--free(virinfo); w_free(virinfo); } else *virname=NULL; do_log(LOG_DEBUG, "avp_scanfile returned '%d'", rez); return rez; } static int init1(void){ int i = -1, len ; struct stat stat_buf; do_log(LOG_DEBUG, "AVP Init1"); if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. we're using " DEFAULT_SOCKET_PATH); config->virusscanner=strdup(DEFAULT_SOCKET_PATH); } while (kav_run[++i].pid) { len=strlen(config->virusscanner); //--if (NodeCtl) free(NodeCtl); //--if (NodePid) free(NodePid); if (NodeCtl) w_free(NodeCtl); if (NodePid) w_free(NodePid); /* Build the Nodes */ //--if ((NodeCtl=malloc(len + strlen(kav_run[i].ctl) + 10))==NULL) return -1; //--if ((NodePid=malloc(len + strlen(kav_run[i].pid) + 10))==NULL) return -1; if ((NodeCtl=w_malloc(len + strlen(kav_run[i].ctl) + 10))==NULL) return -1; if ((NodePid=w_malloc(len + strlen(kav_run[i].pid) + 10))==NULL) return -1; strncpy(NodeCtl, config->virusscanner, len + 1); if (config->virusscanner[len-1]!='/') strcat(NodeCtl, "/"); strncpy(NodePid, NodeCtl, strlen(NodeCtl) + 1); strcat(NodeCtl, kav_run[i].ctl); strcat(NodePid, kav_run[i].pid); len=strlen(NodeCtl); NodeCtl[len+1]='\0'; NodeCtl[len+2]='\0'; do_log(LOG_DEBUG,"Trying %s %s", NodeCtl, NodePid); if (stat(NodeCtl, &stat_buf)==0) break; } if (kav_run[i].pid==NULL) { do_log(LOG_CRIT, "No KAV daemon running"); return 1; } kav_version = kav_run[i].version; do_log(LOG_DEBUG, "Version: %d NoteCtl: %s NodePid: %s", kav_version, NodeCtl, NodePid); connected=-1; avp_fd=-1; do_log(LOG_DEBUG, "AVP Init1 Done"); return 0; } static int init2(struct proxycontext *p){ do_log(LOG_DEBUG, "AVP Init2"); /* Connect to socket */ if (avp_socket_connect(p)!=0) return -1; do_log(LOG_DEBUG, "AVP Init2 Done"); return 0; } static void uninit2(struct proxycontext *p){ avp_socket_close(); } static int scan(struct proxycontext *p, char **virname){ int ret; do_log(LOG_DEBUG, "avpd_new scanner says hello"); ret=avp_scanfile(p, 0, p->scanthis, virname); do_log(LOG_DEBUG, "AVP scanner got %d",ret); if ((ret&0x0f)==3 || (ret&0x0f)==4) ret = SCANNER_RET_VIRUS; /* virus */ else if (ret<0) ret=SCANNER_RET_ERR; else ret = SCANNER_RET_OK; do_log(LOG_DEBUG, "avpd_new scanner says goodbye (ReturnCode=%s)",(ret==SCANNER_RET_OK ? "OK" : (ret==SCANNER_RET_ERR ? "ERROR" : "VIRUS"))); return ret; } scanner_t scanner_avpd_new = { "avpd_new", /* name */ "Kaspersky Anti-Virus 5.x Daemon", /* description */ &init1, /* init1 (once, afer startup) */ &init2, /* init2 (every connection before first mail) */ &scan, /* scan */ &uninit2, /* uninit2 */ NULL, /* uninit1 */ 0 /* dirscan */ }; p3scan-2.3.2/scanner_sample.c0000644000175000001440000001571110347310635014710 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include /* we need p3scan.h */ #include "p3scan.h" /* place some definitions */ #define DEFAULT_SOCKET_PATH "/var/run/xyz-scanner.sock" extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); /* usually we need p3scan's config, so make it global */ struct configuration_t * config; /* if you need some globals, make them static! */ static int connected; struct sockaddr_un scanner_socket; /* if you need some functions, make them static */ static int function(void){ return 0; } /* If you need initialization, use init1 . It is called after config parsing, * setuid and daemonizing. You can check the parameters (config->xxxx), set * defaults if nothing is given, and so on. * * Return values: * -1 : Error. Scanning will be completely disabled. * No further functions (init2, scan, uninit1, uninit2) will be called * 0 : OK */ static int init1(void){ do_log(LOG_DEBUG, "SAMPLE Init1"); if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. we're using " DEFAULT_SOCKET_PATH); config->virusscanner=strdup(DEFAULT_SOCKET_PATH); } do_log(LOG_DEBUG, "AVP Init1 Done"); return 0; } /* If you need initialization before scanning, use init2. Init2 is called before * the first mail has to be scanned (not when a client only checks if he has new * mails). Here you can connect for example to a socket (which makes more sense * then doing that in init1). * * Return values: * -1 : Error. Scanning is temporarily disabled for further mails over that * connection. * No further functions (scan, uninit2) will be called, but uninit1. * 0 : OK */ static int init2(struct proxycontext *p){ do_log(LOG_DEBUG, "Init2"); /* Connect to socket */ if (socket_connect(p)!=0) return -1; do_log(LOG_DEBUG, "Init2 Done"); return 0; } /* if you need to uninitialize what init2 has initialized, use uninit2, which is * called only if init2 is set and init2 was called! */ static void uninit2(struct proxycontext *p){ socket_close(); } /* if you need to uninitialize when pop3vscan terminates, use uninit1, which is * called only if init1 has returned 0 OR was not set. */ static void uninit1(void){ } /* now the scanner... * Depending on demime (can be configured by user) and dirscan (can only be set * below in scanner_t by you) we have to scan the mailfile, a directory or the * deMIMEd files. * * +--------+---------+--------------------------------------------------------+ * | demime | dirscan | | * +--------+---------+--------------------------------------------------------+ * | 0 | 0 | scan is called once, p->scanthis points to p->mailfile | * +--------+---------+--------------------------------------------------------+ * | 1 | 0 | scan is called for every MIME-file | * | | | p->scanthis points to the actual file in maildir | * +--------+---------+--------------------------------------------------------+ * | 1 | 1 | scan is called once, p->scanthis points to maildir | * +--------+---------+--------------------------------------------------------+ * | 0 | 1 | when the user have not set dirscan, you will get | * | | | p->scanthis pointing to mailfile | * +--------+---------+--------------------------------------------------------+ * * You can set virname to a description of the virus. If scan is invoked * multiple times and if you should find more than one virus we append all * virusnames together (seperated with ' & '). virname is not malloced (it * wouldn't be char**). If you can't examime the virusname set it to NULL, when * no virus is found you can let them untouched. * * Return Codes: * SCANNER_RET_VIRUS : The file contains a virus * SCANNER_RET_OK : The file contains NO virus * SCANNER_RET_ERR : Can't say if it's a virus * * If scan is invoked multiple times, we automatically collect the returncodes, * that means that if one of the files has a virus, then anything else is * ignored (further ERRs and OKs). * */ static int scan(struct proxycontext *p, char **virname){ int ret; do_log(LOG_DEBUG, "SAMPLE scanner says hello"); ret=scanfile(p->scanthis, virname); if (ret==3 || ret==4) ret = SCANNER_RET_VIRUS; /* virus */ else if (ret<0) ret=SCANNER_RET_ERR; else ret = SCANNER_RET_OK; do_log(LOG_DEBUG, "SAMPLE scanner says goodbye"); return ret; } /* Tell us your name (used for scannertype) and a brief description (shown at * startup and in --help). Set the functions you need, or just NULL if not (and * remove the above functions). For infos about dirscan see the scan() * description above. * */ scanner_t scanner_sample = { "sample", /* name */ "A sample scanner frontend", /* description */ &init1, /* init1 (once, after startup) */ &init2, /* init2 (every connection before first mail) */ &scan, /* scan */ &uninit2, /* uninit2 */ &uninit1, /* uninit1 */ 0 /* dirscan */ }; /* * to tell p3scan that we exist, add scanner_sample.o to Makefile * (append to OBJECTS) and define scanner_sample as extern in scanner.h and * place them in scannerlist[] * * That's all! Happy coding! :) * */ p3scan-2.3.2/p3scan-ge.mail0000644000175000001440000000161710347310635014176 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Guten Tag %USERNAME%. Diese Nachricht wurde automatisch von P3Scan generiert, welcher auf %HOSTNAME%.%DOMAINNAME% luft und alle eingehenden Emails auf Viren prft. P3Scan hat den Inhalt der Email, die an Sie gesendet wurde ersetzt, da sie einen VIRUS enthalten hatte! Anstelle der infizierten Email wurde Ihnen diese Nachricht gesendet. Sie knnen den Header dieser Nachricht anschauen, um den kompletten Header der infizierten Nachricht zu analysieren. Virus Name: %VIRUSNAME% Absender der Email: %MAILFROM% Gesendet an: %MAILTO% Datum: %MAILDATE% Betreff: %SUBJECT% Verbindungsdetails: %PROTOCOL% von %CLIENTIP%:%CLIENTPORT% nach %SERVERIP%:%SERVERPORT% Backupdatei der Email: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% by Jack S. Lai . p3scan-2.3.2/p3scan-fr.mail0000644000175000001440000000155210347310635014210 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-1" Bonjour %USERNAME%. Ce corps de message a t gnr automatiquement par P3Scan, qui fonctionne sur %HOSTNAME%.%DOMAINNAME% pour scanned tous les courriels entrants. Il replace le corps du message contenant un VIRUS qui vous tait adress! A la place du courriel infect, ce message vous est envoy. %FILESTATUS% Vous pouvez regarder les enttes de ce message pour des informations compltes sur les enttes du message infect. Nom du virus: %VIRUSNAME% Expditeur du courriel: %MAILFROM% Envoy : %MAILTO% Date: %MAILDATE% Object: %SUBJECT% Informations de connexion: %PROTOCOL% from %CLIENTIP%:%CLIENTPORT% to %SERVERIP%:%SERVERPORT% Fichier du message: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% par Jack S. Lai . p3scan-2.3.2/README-ripmime0000644000175000001440000000056410347310635014072 0ustar jlaiusersThe ripMIME[1] library is only needed if your virus scanner cannot natively process email messages (for example: FRISK F-Prot). You can now compile p3scan without the ripmime library: in p3scan.h:138, change #define DEMIME to #undef DEMIME and then in the source directory, rm Makefile and then: ln -sf Makefile-noripmime Makefile [1] http://www.pldaniels.com/ripmime/ p3scan-2.3.2/README-rpm0000644000175000001440000000015710347310635013224 0ustar jlaiusersIf you're upgrading from a release candidate series RPM do it with: rpm -Uvh --force p3scan-1.0-1.i386.rpmp3scan-2.3.2/p3scan.c0000644000175000001440000037171010347310160013102 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ /* List of features wanted (for grep'ing) TODO: Verbosity TODO: Integrity check (check directories/permissions at start) TODO: Wanted: Header parser TODO: Wanted: white-list support TODO: Wanted: no iptables support */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "p3scan.h" #include "getline_ssl.h" #include "parsefile.h" #include "scanner.h" #ifdef DEMIME #include "ripmime/mime.h" #include "ripmime/ripmime-api.h" #endif /* globals */ int numprocs; struct configuration_t * config; /* p->ismail legend: ismail=0 not processing a message - parsing client commands ismail=1 enable message parsing (got RETR/TOP) - start trapping email ismail=2 got "+ok", read the mail into file ismail=3 closed header buffer ismail=4 have entire message, start processing - email complete, start server noop's ismail=5 scanning done, send mail to client */ /* globals / protos */ #ifdef DEMIME /* filecount from ripmime/mime.c */ extern int filecount; /* MIMEH_set_outputdir() from ripmime/MIME_headers.c */ extern int MIMEH_set_outputdir(char *); #endif static int sockfd; /* has to be global, do_sigterm_main() want's to close them */ /* the proxycontext is global for do_sigterm_proxy(). * no other function should use it! */ static struct proxycontext* global_p; static int stralloc_ptr; static char *strings[8]; static int str_tag[8]; static char smtpstr[LEN]; void do_sigterm_proxy2(int signr){ if(config->debug) fprintf(stderr, "do_sigterm_proxy2, signal %i\n", signr); if (global_p == NULL){ if(config->debug) fprintf(stderr,"already uninitialized"); return; } if (signr != -1 && config->debug) fprintf(stderr, "ERR: We cot SIGTERM!\n"); /* sig -1 is ok */ if (global_p->client_fd != -1) close(global_p->client_fd); if (global_p->server_fd != -1) close(global_p->server_fd); if (global_p->scannerinit==SCANNER_INIT_OK && config->scanner->uninit2){ if(config->debug) fprintf(stderr, "calling uninit\n2"); config->scanner->uninit2(global_p); if(config->debug) fprintf(stderr, "uninit2 done\n"); } if(config->debug) fprintf(stderr, "Uninit context\n"); context_uninit(global_p); global_p=NULL; if(config->debug) fprintf(stderr, "context_uninit done, exiting now\n"); if (signr != -1) exit(1); } void do_log(int level, const char *fmt,...){ char puffer[4096]; char timenow[28]; int abortfd=0; time_t now = time(NULL); va_list az; #ifdef DEBUG_MEM struct mallinfo m=mallinfo(); #endif if (!config->debug && level==LOG_DEBUG) return; if (!config->debug && config->quiet && level==LOG_NOTICE) return; va_start(az,fmt); vsnprintf(puffer, 4000, fmt, az); strcpy(timenow,ctime(&now)+ 11); if (!config->debug){ openlog(config->syslogname, LOGOPT, LOGFAC); syslog(LOG_NOTICE, "%s\n", puffer); closelog(); } else { fflush(stdout); fprintf(stderr, #ifdef DTIME "%.8s " #endif "%s[%i]: " #ifdef DEBUG_MEM "Mem: %i " #endif "%s\n", #ifdef DTIME timenow, #endif config->syslogname, getpid(), #ifdef DEBUG_MEM m.uordblks, #endif puffer); fflush(NULL); } if (level==LOG_EMERG){ do_log(LOG_NOTICE, "ERR: Exiting now...\n"); fprintf(stderr, "%s\n", puffer); if (strlen(NONULL(config->emergency))){ snprintf(puffer,4096,"echo '%s' | %s -s 'P3Scan Terminating!' %s", config->emergency, config->mail, config->emergcon); do_log(LOG_DEBUG,"echo '%s' | %s -s 'P3Scan Terminating!' %s", config->emergency, config->mail, config->emergcon); if (system(puffer)) fprintf(stderr,"ERR: Calling do_log!"); } /* Tell main p3scan to abort */ if ((abortfd=open(ABORTFILE, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) >= 0) close(abortfd); va_end(az); if (config->child) { do_sigterm_proxy2(1); } exit(1); } va_end(az); return; } /** * Code By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" */ void * w_malloc(size_t bytes) { void *memory = NULL; // Store the allocated memory here memory_list *new_item = NULL; // Our new item structure memory = calloc(bytes,1); // Try to allocate the memory new_item = calloc(1,sizeof(struct memory_list)); // Then allocate memory for the list item as well if(memory) { // Make sure the first allocation worked if(new_item == NULL) { // Make sure the second allocation worked config->emergency="Memory allocation error, no room for memory list item."; do_log(LOG_EMERG,"ERR: Memory allocation error, no room for memory list item."); return NULL; // We don't actually get here } global_memory_count += bytes + sizeof(struct memory_list); // Increment our global byte count new_item->address = memory; // Initialize the new item's address pointer new_item->size = bytes; // Set the bytes parameter to the size of the buffer in ->address new_item->next = NULL; // Start with a NULL prev and next pointer new_item->prev = NULL; if(memory_list_head) { // If the list already has items new_item->next = memory_list_head; // Then we just add this item to the head of the list memory_list_head->prev = new_item; memory_list_head = new_item; } else { memory_list_head = new_item; // The list didn't have items, we add this as the head } return memory; // Return the allocated memory } else { config->emergency="Memory allocation error, out of memory"; do_log(LOG_EMERG,"ERR: Memory allocation error, out of memory."); // The first allocation failed return NULL; // We don't actually get here } } void w_free(void *f_address, char *loc) { memory_list *temp = NULL,*found = NULL; // Temporary pointers for list manipulation if(f_address == NULL) // Can't free nothing ;) return; for(temp=memory_list_head;temp!=NULL;temp = temp->next) { // Walk the memory list looking for an item if(temp->address == f_address) { // with the same address as we are asked to free found = temp; // and note that we have found it then break; // break from the for loop to save time } } if(!found) { // If we haven't found it, then we shouldn't free it do_log(LOG_CRIT,"ERR: Unable to free memory not previously allocated: %s",loc); // Report this as an error } global_memory_count -= found->size + sizeof(struct memory_list);// Decrement our global byte count free(f_address); // Actually free the data // Then remove the item from the list: if(found->prev) // If there is an item previous to us found->prev->next = found->next; // point it at the next item if(found->next) // If there is an item after us found->next->prev = found->prev; // point it at the previous item if(found == memory_list_head) // If we are the head of the list memory_list_head = found->next; // move the head of the list up one free(found); // Now we can actually free the memory used by our item } void w_free_all(void) { memory_list *temp = NULL; while(memory_list_head) { free(memory_list_head->address); temp = memory_list_head->next; free(memory_list_head); memory_list_head = temp; } } void w_memory_init(void) { static int state = 0; // Initialize a static variable we can use to keep state if(state != 0) // If the variable is not zero then we have already been called return; // do nothing but return // If the variable is 0 then we have not been called before state = 1; // Note that we have now been called memory_list_head = NULL; // Set the head of the memory list to NULL (no items) global_memory_count = 0; // Start the memory allocation count at 0 atexit(w_free_all); // Register to have w_free_all() called at normal termination } /** * End code By Nathan Yocom. */ char *stristr(const char *string, const char *pattern){ char *pptr, *sptr, *start; for (start = (char *)string; *start != NUL; start++){ /* find start of pattern in string */ for ( ; ((*start!=NUL) && (toupper(*start) != toupper(*pattern))); start++); if (NUL == *start) return NULL; pptr = (char *)pattern; sptr = (char *)start; while (toupper(*sptr) == toupper(*pptr)){ sptr++; pptr++; /* if end of pattern then pattern was found */ if (NUL == *pptr) return (start); } } return NULL; } char *substr(char *string, int start, size_t count) { smtpstr[0] = '\0'; /* The NUL string error return */ if (string != NULL) { size_t len = strlen(string); if (start < 0) start = len + start; if (start >= 0 && start < len) { if (count == 0 || count > len - start) count = len - start; if (count < LEN) { strncpy(smtpstr, string + start, count); smtpstr[count] = 0; } } } return smtpstr; } int p3_mkstemp(char *template){ /** mkstemp w/O_SYNC open Somehow this routine (even normal "mkstemp") turns all files in template dir to 0700 even though they are created 0600. */ char *XXXXXX; size_t len; int fd; static uint64_t value; uint64_t random_time_bits; unsigned int attempts_min = 62 * 62 * 62; unsigned int count; static const char letters[] = LETTERS; int save_errno = errno; unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min; len = strlen(template); if (len < 6 || strcmp(&template[len - 6], "XXXXXX")) { errno = EINVAL; return -1; } XXXXXX = &template[len - 6]; /* Get some more or less random data. */ random_time_bits = time(NULL); value += random_time_bits ^ getpid (); for (count = 0; count < attempts; value += 7777, ++count){ uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; fd=open(template, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | O_SYNC, 0600); if (fd >= 0){ errno = save_errno; return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } char *stralloc(size_t length){ register int i; if (UINT_MAX == length) return NULL; /* Assume size_t == unsigned int */ i = stralloc_ptr++; ++length; /* Allow for terminating NUL */ if ((!strings[i]) || (length > strlen(strings[i]))){ strings[i] = (char *)realloc(strings[i], length); assert(NULL != strings[i]); str_tag[i] = -1; } else str_tag[i] = 0; stralloc_ptr &= 7; return (strings[i]); /* Maintains 8 strings in a circular buffer */ } char *right(char *string, size_t i){ char *buf; size_t strlength = strlen(string); if (i > strlength) i = strlength; buf = stralloc(i); strcpy(buf, &string[strlength-i]); return buf; } char *strreplace(char *haystack,char *needle,char *rstr){ size_t size=strlen(haystack)-strlen(needle)+strlen(rstr)+1; char *p=w_malloc(size * sizeof(char)); char *ptrp=p; char *newstr=haystack; char *match; char *replace; int i,j; while (*newstr){ match=needle; replace=rstr; i=0; while(*newstr && *match){ if(*newstr != *match){ *ptrp++=*newstr++; match=needle; i=0; } else if(*newstr==*match){ *ptrp++=*newstr++; match++; i++; } } if(i==(int)strlen(needle)){ j=0; while(j -1 && n < size) return msg; /* Else try again with more space. */ if (n > -1) size = n+1; /* glibc 2.1 - precisely what is needed*/ else size *= 2; /* glibc 2.0 - twice the old size*/ if ((msg = realloc (msg, size)) == NULL) return NULL; } } int scan_directory(struct proxycontext *p){ int ret, ret_all; DIR *dp; struct dirent *de; struct stat s; char *file; int maildirlen; char * virname; #define VISIZE 1000 char *vi; int vipos = 0; /* scan directory */ maildirlen=strlen(p->maildir); if (stat (p->maildir, &s) == -1){ context_uninit(p); config->emergency=make_message("%s does not exist", p->maildir); do_log(LOG_EMERG, "ERR: %s does not exist", p->maildir); return SCANNER_RET_ERR; } if (!S_ISDIR(s.st_mode)){ context_uninit(p); config->emergency=make_message("%s is not a directory", p->maildir); do_log(LOG_EMERG, "ERR: %s is not a directory", p->maildir); return SCANNER_RET_ERR; } if ((dp = opendir (p->maildir)) == NULL){ context_uninit(p); config->emergency=make_message("Can't open directory %s", p->maildir); do_log(LOG_EMERG, "ERR: Can't open directory %s", p->maildir); return SCANNER_RET_ERR; } vi=w_malloc(VISIZE); ret_all=SCANNER_RET_OK; vi[0]='\0'; while ((de = readdir (dp)) != NULL){ if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; file=w_malloc(maildirlen + strlen(de->d_name) +2); sprintf(file, "%s/%s", p->maildir, de->d_name); if (lstat (file, &s) == -1){ context_uninit(p); w_free(file,"file1"); config->emergency=make_message("Can't get file info for %s - I won't touch it.", file); do_log(LOG_EMERG, "ERR: Can't get file info for %s - I won't touch it.", file); continue; } if (!S_ISREG(s.st_mode)){ context_uninit(p); config->emergency=make_message("%s is not a regular file. I won't touch it.", file); do_log(LOG_EMERG, "ERR: %s is not a regular file. I won't touch it.", file); w_free(file,"file2"); continue; } /* build filename */ do_log(LOG_DEBUG, "Going to scan '%s'", file); p->scanthis=file; virname=NULL; ret=config->scanner->scan(p, &virname); if (ret==SCANNER_RET_VIRUS){ ret_all=SCANNER_RET_VIRUS; if (virname && strlen(virname)4){ vi[vipos-3]='\0'; p->virinfo=vi; } else p->virinfo=NULL; return ret_all; } /* clean_child_directory -- iterate through directory * removing all files and subdirectories * * args: * childpid -- used to construct directory path * to erase. If called by child pass * getpid() * * returns: * 0 -- OK * 1 -- mem allocation error * * note: do_log(LOG_EMERG, ...) currently * doesn't return!!! */ int clean_child_directory(pid_t childpid){ DIR *dp; struct dirent *de; char *dir,*file; int dirlen,filelen; dirlen=strlen(config->virusdirbase)+20; dir=w_malloc(dirlen); snprintf(dir, dirlen, "%s/children/%d/", config->virusdirbase, childpid); /* Erase directory if it exists */ if ((dp = opendir (dir)) != NULL){ do_log(LOG_DEBUG, "Erasing %s contents", dir); while ((de = readdir (dp)) != NULL){ if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; filelen=dirlen + strlen(de->d_name) + 2; file=w_malloc(filelen); snprintf(file, filelen, "%s%s", dir, de->d_name); do_log(LOG_DEBUG, "Unlinking (%s)", file); if ((unlink(file)<0)){ do_log(LOG_CRIT, "ERR: File Error! Could not erase %s",file); return 1; } w_free(file,"file4"); } closedir (dp); do_log(LOG_DEBUG, "Removing directory %s", dir); if ((rmdir(dir)<0)){ do_log(LOG_CRIT, "ERR: Directory Error! Could not erase %s",dir); return 1; } } w_free(dir,"dir"); return 0; } int checktimeout(struct proxycontext *p){ int readerr=0, senterr=0; char svrout[1]; if (p->cksmtp) return 0; if (config->enabletop){ if (p->topping && p->posttop) return 0; } if (p->now+config->timeoutismail != 5){ // Are we sending message to client? if (config->broken){ /* Line parsing */ if (!p->gobogus) readerr=getlinep3(p->header_fd,p->hdrbuf); if (readerr>=0 && !p->gobogus){ senterr=writeline(p->client_fd, WRITELINE_LEADING_RN, p->hdrbuf->line); if (senterr < 0) return senterr; do_log(LOG_DEBUG, "Timeout: Sent client a line: %s", p->hdrbuf->line); p->hdroffset++; }else{ /* End of hdrbuf! We are still parsing! */ if (!p->gobogus) p->gobogus=1; senterr=writeline(p->client_fd, WRITELINE_LEADING_RN, BOGUSX); if (senterr < 0) return senterr; do_log(LOG_DEBUG, "Timeout: Sent client a bogus line."); } } else { /* Character parsing */ if (!p->gobogus) readerr=read(p->header_fd,p->cbuff,1); if (readerr>=0 && !p->gobogus){ senterr=secure_write(p->client_fd,p->cbuff,1); if (senterr < 0) return senterr; do_log(LOG_DEBUG, "Timeout: Sent client a character: %s",p->cbuff); p->hdroffset++; }else{ /* End of hdrbuff! We are still parsing! */ p->gobogus=1; if (p->boguspos < 74){ svrout[0]=BOGUSX[p->boguspos]; senterr=secure_write(p->client_fd,svrout,1); if (senterr < 0) return senterr; do_log(LOG_DEBUG, "Timeout: Sent client a character: %s",svrout); p->boguspos++; }else{ if (p->boguspos < 422){ senterr=secure_write(p->client_fd,PERIOD,1); if (senterr < 0) return senterr; p->boguspos++; }else{ senterr=writeline(p->client_fd,WRITELINE_LEADING_N,PERIOD); if (senterr < 0) return senterr; p->boguspos=0; } } } } } /* Only send NOOP when we are processing or sending to client*/ if (p->ismail > 3){ do_log(LOG_DEBUG, "Sending server a NOOP..."); if (p->usessl) senterr=writeline_ssl(p->ssl, WRITELINE_LEADING_RN, SVRCMD); else senterr=writeline(p->server_fd, WRITELINE_LEADING_RN, SVRCMD); if (senterr < 0) return senterr; p->noop++; } do_log(LOG_DEBUG, "Reseting time..."); p->now = time(NULL); } return 0; } int checkbuff(int fdc) { fd_set rfds; struct timeval tv; int retval,fdc2; FD_ZERO(&rfds); FD_SET(fdc, &rfds); tv.tv_sec = 0; tv.tv_usec = 10000; fdc2=fdc+1; retval = select(fdc2, &rfds, NULL, NULL, &tv); if (retval == -1) return 2; else if (retval) return 1; return 0; } int scan_mailfile(struct proxycontext *p){ int ret=0, ret2=0, viret=0, fdc=0; #ifdef DEMIME DIR *dp; struct dirent *de; int maildirlen=0; char *file; #endif int spamfd=-1; unsigned long len=0, kbfree=0; char spmcmd[512]; char newmsg[512]; //char vnmsg[512]; #define NEWMSG "newmsg " #define ALTMSG "vnmsg " #define COPYMSG "/var/spool/p3scan/copymsg " FILE * scanner; static char line[4096*16]; struct statvfs fs; int htmlfd=0; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; /* See if we want to manipulate the virus notification message before it might be sent */ if (config->altemail){ len=strlen(config->virusdir)+strlen(ALTMSG); snprintf(p->vnmsg, len, "%s%s", config->virusdir,ALTMSG); len=strlen(COPYIT)+1+strlen(config->virustemplate)+1+strlen(p->vnmsg)+1; snprintf(spmcmd, len, "%s %s %s",COPYIT,config->virustemplate,p->vnmsg); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't '%s' !!!", spmcmd); return SCANNER_RET_ERR; } } /* See if we have enough room to process the message based upon what the user determines is enough room in p3scan.conf This was already done... but it is also dynamic so check again. */ if ( statvfs( config->virusdir, &fs ) == SCANNER_RET_ERR){ p->errmsg=1; context_uninit(p); config->emergency="Unable to get available space!"; do_log(LOG_EMERG, "ERR: Unable to get available space!"); return SCANNER_RET_CRIT; // Should never reach here, but keep it clean. :) } if (fs.f_bsize==1024) kbfree=fs.f_bavail; else kbfree=fs.f_bsize * (fs.f_bavail / 1024) + fs.f_bavail%1024 * fs.f_bsize / 1024; if ( config->freespace != 0 && kbfree < config->freespace ){ p->errmsg=1; config->emergency=make_message("Not enough space! Available space: %lu", kbfree); do_log(LOG_CRIT, "ERR: Not enough space! Available space: %lu", kbfree); return SCANNER_RET_CRIT; } /* Do we want to rename attachments? */ if (config->renattach != NULL && !p->cksmtp){ ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; /* Only rename non-infected attachments */ len=strlen(config->virusdir)+strlen(NEWMSG); snprintf(newmsg, len, "%s%s", config->virusdir,NEWMSG); if ((spamfd=open(newmsg,O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))<0){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't create newmsg!"); return SCANNER_RET_CRIT; } len=strlen(config->renattach)+strlen(" < ")+strlen(p->mailfile)+strlen(" 2>&1 "); snprintf(spmcmd, len, "%s < %s 2>&1", config->renattach, p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't start renattach '%s' !!!", spmcmd); return SCANNER_RET_ERR; } /* call made, check timeout until data returned */ fdc=fileno(scanner); ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; while (!ret2){ ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } while ((fgets(line, 4095, scanner))!=NULL){ line[strlen(line)-1]='\0'; #ifdef DEBUG_SCANNING do_log(LOG_DEBUG, "AttachLine: '%s'", line); #endif writeline(spamfd, WRITELINE_LEADING_N, line); ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } if((spamfd=close(spamfd))<0){ p->errmsg=1; context_uninit(p); config->emergency=make_message("Can't close newmsg: %s", newmsg); do_log(LOG_EMERG, "ERR: Can't close newmsg: %s", newmsg); return SCANNER_RET_CRIT; } ret=pclose(scanner); len=strlen(MOVEIT)+1+strlen(newmsg)+1+strlen(p->mailfile)+1; snprintf(spmcmd, len, "%s %s %s",MOVEIT,newmsg,p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ do_log(LOG_ALERT, "ERR: Can't '%s' !!!", spmcmd); p->errmsg=1; return SCANNER_RET_ERR; } ret=pclose(scanner); } /* end renattach */ /* perform doctor work (scan for a virus) */ ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; p->virinfo=NULL; #ifdef DEMIME if (config->demime){ /* extract MIME Parts into maildir */ do_log(LOG_DEBUG, "DeMIMEing to %s", p->maildir); viret = mkdir(p->maildir, S_IRWXU); if ((viret == -1)&&(errno != EEXIST)){ do_log(LOG_CRIT, "ERR: Cannot create directory '%s' (%s). Can't scan mail.\n", p->maildir, strerror(errno)); p->errmsg=1; return SCANNER_RET_CRIT; } MIMEH_set_outputdir( p->maildir ); MIME_init(); MIME_unpack( p->maildir, p->mailfile, 0); MIME_close(); /* TODO: ripmime error checking */ p->scanthis = p->maildir; /* SCAN */ if (config->scanner->dirscan){ /* scanner wants to scan the directory itself, so call it once */ /* give directory name to scanner, if he want (basic) */ do_log(LOG_DEBUG, "Invoking scanner"); p->virinfo=NULL; viret=config->scanner->scan(p, &p->virinfo); } else { /* give all files to scanner * also connect all virusinfos to one string */ viret=scan_directory(p); } do_log(LOG_DEBUG, "Scanner returned %i", viret); /* unlinking MIME-files */ do_log(LOG_DEBUG, "Unlinking deMIMEd files", p->maildir); maildirlen=strlen(p->maildir); if ((dp = opendir (p->maildir)) == NULL){ p->errmsg=1; context_uninit(p); config->emergency=make_message("Can't open directory %s to erase files", p->maildir); do_log(LOG_EMERG, "ERR: Can't open directory %s to erase files", p->maildir); } else { while ((de = readdir (dp)) != NULL){ if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; file=w_malloc(maildirlen + strlen(de->d_name) +2); sprintf(file, "%s/%s", p->maildir, de->d_name); if ((unlink(file)<0)){ config->emergency=make_message("File Error! Could not erase %s",file); do_log(LOG_EMERG, "ERR: File Error! Could not erase %s",file); } w_free(file,"file5"); } closedir (dp); rmdir(p->maildir); } } else { /* if config->demime */ #endif /* no demime */ p->scanthis = p->mailfile; /* invoke configured scanner */ do_log(LOG_DEBUG, "Invoking scanner"); p->virinfo=NULL; viret=config->scanner->scan(p, &p->virinfo); do_log(LOG_DEBUG, "Scanner returned %i", viret); /* TODO: Fail on unexpected scanner return code. */ #ifdef DEMIME } #endif if (p->virinfo){ TRIM(p->virinfo); } ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; /* Do we want to scan for spam? */ if (config->checkspam && !config->ispam && !viret && !p->cksmtp){ /* Ok, first, create a new message */ do_log(LOG_DEBUG, "Checking for spam"); len=strlen(config->virusdir)+strlen(NEWMSG); snprintf(newmsg, len, "%s%s", config->virusdir,NEWMSG); if ((spamfd=open(newmsg,O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))<0){ do_log(LOG_ALERT, "ERR: Can't create newmsg!"); p->errmsg=1; return SCANNER_RET_CRIT; } /* First, find out if we need to replace "dspamuser" */ if (strstr(config->spamcheck,"dspamuser")!=NULL){ if (strlen(NONULL(p->dspamuser))) config->spamcheck=strreplace(config->spamcheck,"dspamuser",p->dspamuser); } /* Now call anti-spam program */ len=strlen(config->spamcheck)+strlen(" < ")+strlen(p->mailfile)+strlen(" 2>&1 "); /* Trap output to a buffer. */ snprintf(spmcmd, len, "%s < %s 2>&1", config->spamcheck, p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't start spammer '%s' !!!", spmcmd); return SCANNER_RET_ERR; } /* call made, check timeout until data returned */ fdc=fileno(scanner); ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; while (!ret2){ ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } while ((fgets(line, 4095, scanner))!=NULL){ ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; line[strlen(line)-1]='\0'; /* write the buffer to our new message */ #ifdef DEBUG_SCANNING do_log(LOG_DEBUG, "SpammerLine: '%s'", line); #endif ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; if ((strncmp(line,".",1 ) == 0 && strlen(line) == 1)){ do_log(LOG_DEBUG, "Not writing '.'"); } else if ((strncmp(line,".\r",2) == 0 && strlen(line) == 2)){ do_log(LOG_DEBUG, "Not writing '.'"); } else if (strncmp(line,"..",2) == 0 && strlen(line) == 2 && strstr(config->spamcheck,"dspam")!=NULL){ do_log(LOG_DEBUG, "Not writing dspam '..'"); } else { writeline(spamfd, WRITELINE_LEADING_N, line); } } do_log(LOG_DEBUG, "Writing new ."); writeline(spamfd, WRITELINE_LEADING_RN, "."); if((spamfd=close(spamfd))<0){ context_uninit(p); config->emergency = "Can't close newmsg spamfd"; do_log(LOG_EMERG, "ERR: Can't close newmsg spamfd"); return SCANNER_RET_CRIT; } ret=pclose(scanner); /* Spam report is now in $virusdir/newmsg so now replace original msg with newmsg */ len=strlen(MOVEIT)+1+strlen(newmsg)+1+strlen(p->mailfile)+1; snprintf(spmcmd, len, "%s %s %s",MOVEIT,newmsg,p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't '%s' !!!", spmcmd); return SCANNER_RET_ERR; } ret=pclose(scanner); } /* End of spam checking */ /* Start HTML parsing */ if (config->overwrite && !viret && !p->cksmtp){ do_log(LOG_DEBUG,"HTML Parsing now..."); ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; /* Do not parse infected mail as client will not see it anyway. */ len=strlen(config->virusdir)+strlen(NEWMSG); snprintf(newmsg, len, "%s%s", config->virusdir,NEWMSG); if ((htmlfd=open(newmsg,O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))<0){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't create newmsg!"); return SCANNER_RET_CRIT; } /* First, find out if we need to replace "htmluser" */ if (strstr(config->overwrite,"htmluser")!=NULL){ config->overwrite=strreplace(config->overwrite,"htmluser",p->dspamuser); } /* Now call HTML Parsing program */ len=strlen(config->overwrite)+strlen(" < ")+strlen(p->mailfile)+strlen(" 2>&1 "); /* Trap parser program output to a buffer. */ snprintf(spmcmd, len, "%s < %s 2>&1", config->overwrite, p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't start HTML parser '%s' !!!", spmcmd); return SCANNER_RET_ERR; } /* call made, check timeout until data returned */ fdc=fileno(scanner); ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; while (!ret2){ ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } while ((fgets(line, 4095, scanner))!=NULL){ line[strlen(line)-1]='\0'; /* write the buffer to our new message */ #ifdef DEBUG_SCANNING do_log(LOG_DEBUG, "HTML Line: '%s'", line); #endif writeline(htmlfd, WRITELINE_LEADING_N, line); ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } if((htmlfd=close(htmlfd))<0){ context_uninit(p); sprintf(config->emergency,"Can't close newmsg htmlfd"); do_log(LOG_EMERG, "ERR: Can't close newmsg htmlfd"); return SCANNER_RET_CRIT; } /* See if the call worked */ ret=pclose(scanner); if(ret==0){ len=strlen(MOVEIT)+1+strlen(newmsg)+1+strlen(p->mailfile)+1; snprintf(spmcmd, len, "%s %s %s",MOVEIT,newmsg,p->mailfile); if ((scanner=popen(spmcmd, "r"))==NULL){ p->errmsg=1; do_log(LOG_ALERT, "ERR: Can't '%s' !!!", spmcmd); return SCANNER_RET_ERR; } ret=pclose(scanner); } else do_log(LOG_ALERT, "ERR: HTML Parser returned error! Ignoring it's output!"); } /* End HTML parsing */ if (strlen(NONULL(p->virinfo))<1){ if (p->virinfo) w_free(p->virinfo,"p->virinfo"); p->virinfo=strdup(MESSAGE_NOVIRINFO); } ret=viret; return ret; } unsigned long send_mailfile(char * mailfile, int fd, struct proxycontext *p){ struct linebuf *filebuf, *footbuf; int mailfd, footfd; int res=0, sendret=0, gotprd=0, gottxt=0, nogo=0; unsigned long len=0; char svrout[1]; if ((mailfd=open(mailfile, O_RDONLY ))<0){ context_uninit(p); config->emergency=make_message("Can't open mailfile (%s)!", mailfile); do_log(LOG_EMERG, "ERR: Can't open mailfile (%s)!", mailfile); return 0; } filebuf=linebuf_init(16384); footbuf=linebuf_init(512); if (!filebuf){ close(mailfd); close(fd); context_uninit(p); config->emergency="Could not allocate memory for sending mail!"; do_log(LOG_EMERG, "ERR: Could not allocate memory for sending mail!"); } gotprd=0; /* advance to mailfd pointer to past data already sent: */ if (config->broken){ if(p->hdroffset && !p->gobogus){ while (p->hdroffset){ res=getlinep3(mailfd, filebuf); p->hdroffset--; } } } else { if(p->hdroffset){ lseek(mailfd, p->hdroffset, SEEK_SET); } /* See if bogus headerline sent */ if (p->gobogus){ if (p->boguspos < 91){ svrout[0]=BOGUSX[p->boguspos]; secure_write(p->client_fd,svrout,1); p->boguspos++; } /* now close it */ writeline(p->client_fd,WRITELINE_LEADING_RN,PERIOD); p->gobogus=0; } } while (1){ sendret=checktimeout(p); if (sendret==GETLINE_PIPE){ do_log(LOG_CRIT, "ERR: Client disappeared during mail send!"); linebuf_uninit(filebuf); return EPIPE; } else if (sendret){ context_uninit(p); linebuf_uninit(filebuf); config->emergency="Sending mail to client"; do_log(LOG_EMERG, "ERR: Sending mail to client"); /* we are dead now. Should not reach here. But allow it to fall through in case LOG_EMERG is changed in the future. */ return 1; } if ((res=getlinep3(mailfd, filebuf))<0){ if (res==GETLINE_TOO_LONG){ /* Buffer contains part of line, take care of later */ } else { /* Other error, take care of later */ break; } } if (filebuf->linelen >=0 ){ len += filebuf->linelen; #ifdef DEBUG_MESSAGE do_log(LOG_DEBUG, ">%s", filebuf->line); #endif if ((strncmp(filebuf->line,".",1 ) == 0 && strlen(filebuf->line) == 1)) gotprd=1; if ((strncmp(filebuf->line,".\r",2) == 0 && strlen(filebuf->line) == 2)) gotprd=1; //if ((strncmp(filebuf->line,"Content-Type: application/pgp-signature",39)==0 && strlen(filebuf->line)==39)) gotpgp=1; //if ((strncmp(filebuf->line,"Content-Type: application/pgp-signature",39)==0 && strlen(filebuf->line)==39)) nogo=1; if (strncmp(filebuf->line,"Content-Type: ",14)==0){ if ((strncmp(filebuf->line,"Content-Type: text/plain;",25)==0 && strlen(filebuf->line)==25)) gottxt=1; else nogo=1; } if (p->cksmtp && gotprd && !nogo){ if ((footfd=open(FOOTER,O_RDONLY))>=0){ sendret=writeline(fd, WRITELINE_LEADING_RN, "**********"); while(1){ if ((sendret=getlinep3(footfd, footbuf))<0){ break; } if (footbuf->linelen >=0 ){ sendret=writeline(fd, WRITELINE_LEADING_RN, footbuf->line); } } close(footfd); writeline_format(fd, WRITELINE_LEADING_RN,PROGNAME" "VERSION" running on %s.%s", paramlist_get(p->params, "%HOSTNAME%"),paramlist_get(p->params, "%DOMAINNAME%")); sendret=writeline_format(fd,WRITELINE_LEADING_RN,"%s",paramlist_get(p->params,"%VDINFO%")); sendret=writeline(fd,WRITELINE_LEADING_RN,"**********"); } } /* Take care of buffer here */ if (res==GETLINE_TOO_LONG){ sendret=writeline(fd, WRITELINE_LEADING_NONE, filebuf->line); } else { if (!p->cksmtp) sendret=writeline(fd, WRITELINE_LEADING_RN, filebuf->line); else if (!gotprd) sendret=writeline(fd, WRITELINE_LEADING_RN, filebuf->line); } if (sendret==GETLINE_PIPE){ do_log(LOG_CRIT, "ERR: Client disappeared during mail send!"); linebuf_uninit(filebuf); return EPIPE; } else if (sendret){ context_uninit(p); linebuf_uninit(filebuf); config->emergency="Sending mail to client"; do_log(LOG_EMERG, "ERR: Sending mail to client"); /* we are dead now. Should not reach here. But allow it to fall through in case LOG_EMERG is changed in the future. */ return 1; } } } if (res!=GETLINE_EOF){ do_log(LOG_CRIT, "ERR: reading from mailfile %s, error code: %d", mailfile, res); linebuf_uninit(filebuf); return 1; } if (!gotprd){ do_log(LOG_DEBUG, "Wrote new EOM."); writeline(fd, WRITELINE_LEADING_N, "."); } linebuf_uninit(filebuf); close(mailfd); return len; } void set_defaultparams(struct proxycontext * p){ char buf[256]; FILE * scanner; static char line[512], virdef[512]; int len=0, vlen=0; char vcmd[512]; gethostname(buf, 256); paramlist_set(p->params, "%HOSTNAME%", buf); if(getdomainname(buf, 256)) do_log(LOG_CRIT,"ERR: getdomainname"); paramlist_set(p->params, "%DOMAINNAME%", buf); paramlist_set(p->params, "%PROGNAME%", PROGNAME); paramlist_set(p->params, "%VERSION%", VERSION); paramlist_set(p->params, "%CLIENTIP%", inet_ntoa(p->client_addr.sin_addr)); sprintf(buf, "%i", ntohs(p->client_addr.sin_port)); paramlist_set(p->params, "%CLIENTPORT%", buf); paramlist_set(p->params, "%SERVERIP%", inet_ntoa(p->server_addr.sin_addr)); sprintf(buf, "%i", ntohs(p->server_addr.sin_port)); paramlist_set(p->params, "%SERVERPORT%", buf); if (p->cksmtp) paramlist_set(p->params, "%PROTOCOL%", "SMTP"); else if (p->usessl) paramlist_set(p->params, "%PROTOCOL%", "POP3S"); else paramlist_set(p->params, "%PROTOCOL%", "POP3"); if(config->footer!=NULL){ len=strlen(config->footer); snprintf(vcmd, len+1, "%s 2>&1", config->footer); if ((scanner=popen(vcmd, "r"))==NULL){ paramlist_set(p->params, "%VDINFO%", NULL); do_log(LOG_CRIT, "ERR: Can't get scanner version '%s' !!!", vcmd); }else{ while ((fgets(line, 512, scanner))!=NULL){ len=strlen(line); if ((len+vlen) < 512) { if (len > 1) strcat(&virdef[vlen],line); else if (line[0] != '\r' && line[0] != '\n') strcat(&virdef[vlen],line); else vlen--; } vlen=vlen+len; } pclose(scanner); if (vlen < 512) virdef[vlen-1]='\0'; else virdef[512]='\0'; paramlist_set(p->params, "%VDINFO%", virdef); } } else paramlist_set(p->params, "%VDINFO%", NULL); } void set_maildateparam(struct paramlist * params){ char buf[256]; int diff_hour, diff_min; time_t now = time(NULL); struct tm *tm = localtime(&now); struct tm local; struct tm *gmt; int len; memcpy(&local, tm, sizeof(struct tm)); gmt = gmtime(&now); diff_min = 60*(local.tm_hour - gmt->tm_hour) + local.tm_min - gmt->tm_min; if (local.tm_year != gmt->tm_year) diff_min += (local.tm_year > gmt->tm_year)? 1440 : -1440; else if (local.tm_yday != gmt->tm_yday) diff_min += (local.tm_yday > gmt->tm_yday)? 1440 : -1440; diff_hour = diff_min/60; diff_min = abs(diff_min - diff_hour*60); len = strftime(buf, sizeof(buf), "%a, ", &local); (void) sprintf(buf + len, "%02d ", local.tm_mday); len += strlen(buf + len); len += strftime(buf + len, sizeof(buf) - len, "%b %Y %H:%M:%S", &local); (void) sprintf(buf + len, " %+03d%02d", diff_hour, diff_min); paramlist_set(params, "%MAILDATE%", buf); } void set_paramsfrommailheader(char * mailfile, struct paramlist * params, struct proxycontext *p){ struct linebuf *lb; int fd=0; char * c; if ((fd=open(mailfile, O_RDONLY ))<0) return; lb=linebuf_init(65535); while (getlinep3(fd, lb)>=0){ if (lb->linelen >0 ){ if (!strncasecmp(lb->line, "from: ", 6)){ c=lb->line+6; TRIM(c); paramlist_set(params, "%MAILFROM%", c); } else if (!strncasecmp(lb->line, "subject: ", 9)) { c=lb->line+9; TRIM(c); paramlist_set(params, "%SUBJECT%", c); } else if (!strncasecmp(lb->line, "To: ", 4)) { c=lb->line+4; TRIM(c); paramlist_set(params, "%MAILTO%", c); } else if (!strncasecmp(lb->line, "X-RCPT-TO: ", 11)) { if (!paramlist_get(params, "%USERNAME%")){ c=lb->line+11; TRIM(c); c = substr(c,1,(strlen(c)-2)); paramlist_set(params, "%USERNAME%", c); if (!p->dspamuser) p->dspamuser=paramlist_get(params,"%USERNAME%"); } } } else if (lb->linelen == 0) break; // only the header } linebuf_uninit(lb); close(fd); } void unset_paramsfrommailheader(struct paramlist * params){ paramlist_set(params, "%MAILFROM%", NULL); paramlist_set(params, "%SUBJECT%", NULL); paramlist_set(params, "%MAILTO%", NULL); paramlist_set(params, "%VIRUSNAME%", NULL); paramlist_set(params, "%MAILFILE%", NULL); paramlist_set(params, "%P3SCANID%", NULL); paramlist_set(params, "%VDINFO%", NULL); } int do_virusaction(struct proxycontext * p){ struct linebuf *ckbuf; char *mail=NULL, *mailx=NULL; char svrout[1]; char comm[4096]; unsigned long len; int readerr=0, bufferr=0, subjfd=-1, extrafd=-1; int ret; #define CHMODCMD "/bin/chmod 0600" if (p->cksmtp){ do_log(LOG_WARNING,"554 %s",config->smtprset); writeline_format(p->client_fd, WRITELINE_LEADING_RN, "554 %s",config->smtprset); writeline_format(p->server_fd, WRITELINE_LEADING_RN, "RSET"); writeline_format(p->server_fd, WRITELINE_LEADING_RN, "QUIT"); return 0; } /* complete the header of the original message... */ if (config->broken){ if(p->hdroffset && !p->gobogus){ while ((readerr=getlinep3(p->header_fd,p->hdrbuf)!=GETLINE_EOF)){ writeline(p->client_fd, WRITELINE_LEADING_RN, p->hdrbuf->line); } } else { while ((readerr=read(p->header_fd,p->cbuff,1)>0)){ if (readerr < 0){ context_uninit(p); config->emergency="Could not read header file (broken)!"; do_log(LOG_EMERG, "ERR: Could not read header file (broken)!"); } bufferr=secure_write(p->client_fd,p->cbuff,1); if (bufferr==GETLINE_ERR) { context_uninit(p); config->emergency="Could not flush header buffer to client (broken)!"; do_log(LOG_EMERG, "ERR: Could not flush header buffer to client (broken)!"); } } } } else { if (p->gobogus){ if (p->boguspos < 74){ svrout[0]=BOGUSX[p->boguspos]; secure_write(p->client_fd,svrout,1); p->boguspos++; } /* now close it */ writeline(p->client_fd,WRITELINE_LEADING_RN,PERIOD); p->gobogus=0; }else{ while ((readerr=read(p->header_fd,p->cbuff,1)>0)){ if (readerr < 0){ context_uninit(p); config->emergency="Could not read header file!"; do_log(LOG_EMERG, "ERR: Could not read header file!"); } if ((strncmp(p->cbuff,"\r",1)==0) || (strncmp(p->cbuff,"\n",1)==0) ) bufferr=secure_write(p->client_fd,"\r\n",2); else if ((strncmp(p->cbuff,"\r",1)!=0) && (strncmp(p->cbuff,"\n",1)!=0)) bufferr=secure_write(p->client_fd,p->cbuff,1); if (bufferr==GETLINE_ERR) { context_uninit(p); config->emergency="Could not flush header buffer to client!"; do_log(LOG_EMERG, "ERR: Could not flush header buffer to client!"); } } } } if (!p->hdrdate) writeline_format(p->client_fd,WRITELINE_LEADING_RN,"Date: %s", paramlist_get(p->params, "%MAILDATE%")); if (!p->hdrfrom) writeline_format(p->client_fd,WRITELINE_LEADING_RN,"From: %s", paramlist_get(p->params, "%MAILFROM%")); if (!p->hdrto) writeline_format(p->client_fd,WRITELINE_LEADING_RN,"To: %s", paramlist_get(p->params, "%MAILTO%")); p->hdroffset=0; p->extra=0; mail = w_malloc(strlen(p->mailfile)+10); if ((extrafd=open(EXTRA,O_RDONLY))>=0) { ckbuf=linebuf_init(128); if ((readerr=getlinep3(extrafd,ckbuf)!=GETLINE_EOF)){ p->extra=1; mailx = w_malloc(strlen(p->mailfile)+10); } else do_log(LOG_NOTICE, "ERR: p3scan.extra should not be blank"); linebuf_uninit(ckbuf); close(extrafd); } /* If mail is infected AND we just want to delete it, just don't move it */ if (!config->delit){ snprintf(comm,4096,"%s %s %s",MOVEIT,p->mailfile,config->virusdirbase); if(system(comm)) do_log(LOG_CRIT,"ERR: move"); sync(); snprintf(comm,4096,"%s %s/p3scan.*",CHMODCMD,config->virusdirbase); do_log(LOG_DEBUG,"Forcing all files 0600 %s",comm); if(system(comm)) do_log(LOG_CRIT,"ERR: chmod"); } sprintf(mail, "%s/%i.mailout", config->notifydir,getpid()); if (p->extra) sprintf(mailx, "%s/%i.extrout", config->notifydir,getpid()); if (!config->altemail){ subjfd = open(config->virustemplate, O_RDONLY); if (subjfd<0){ w_free(mail,"mail0"); if (p->extra) w_free(mailx,"mailx0"); context_uninit(p); config->emergency=make_message("Critical error opening file '%s', Program aborted.", p->vnmsg); do_log(LOG_EMERG,"ERR: Critical error opening file '%s', Program aborted.", p->vnmsg); /* should not reach here as we are dead */ } readerr=read(subjfd,comm,4096); /* Check for Subject line... */ if (strncmp(comm,"Subject:",8)!=0) writeline_format(p->client_fd,WRITELINE_LEADING_RN,"Subject: %s %s",config->subject,paramlist_get(p->params, "%VIRUSNAME%")); close(subjfd); } if (p->extra) { if ((ret = parsefile(EXTRA, mailx, p->params, WRITELINE_LEADING_RN))!=0) { context_uninit(p); unlink(mail); w_free(mail,"mail1"); if (p->extra) { unlink(mailx); w_free(mailx,"mailx1"); } if (ret<0) { config->emergency=make_message("Can't open extra mail notification template %s", EXTRA); do_log(LOG_EMERG, "ERR: Can't open extra mail notification template %s",EXTRA); } else { config->emergency=make_message("Can't creade extra virus warning mail message %s", p->mailfile); do_log(LOG_EMERG, "ERR: Can't create extra virus warning mail message %s",p->mailfile); } return -1; } } if (config->altemail){ if ((ret = parsefile(p->vnmsg, mail, p->params, WRITELINE_LEADING_RN))!=0) { context_uninit(p); unlink(mail); w_free(mail,"mail2"); if (p->extra) { unlink(mailx); w_free(mailx,"mailx2"); } if (ret<0) { config->emergency=make_message("Can't open alternate mail notification template %s", p->vnmsg); do_log(LOG_EMERG, "ERR: Can't open alternate mail notification template %s",p->vnmsg); } else { config->emergency=make_message("Can't create virus warning mail message %s", p->mailfile); do_log(LOG_EMERG, "ERR: Can't create virus warning mail message %s",p->mailfile); } return -1; } do_log(LOG_DEBUG,"Sending alternate virus notification: %s",p->vnmsg); } else { if ((ret = parsefile(config->virustemplate, mail, p->params, WRITELINE_LEADING_RN))!=0) { context_uninit(p); unlink(mail); w_free(mail,"mail3"); if (p->extra) { unlink(mailx); w_free(mailx,"mailx3"); } if (ret<0) { config->emergency=make_message("Can't open mail notification template %s", config->virustemplate); do_log(LOG_EMERG, "ERR: Can't open mail notification template %s",config->virustemplate); } else { config->emergency=make_message("Can't create virus warning mail message %s", p->mailfile); do_log(LOG_EMERG, "ERR: Can't create virus warning mail message %s",p->mailfile); } return -1; } } do_log(LOG_DEBUG, "sending new mail"); len=send_mailfile(mail, p->client_fd,p); p->bytecount+=len; /* Send it to extra notification? */ if (config->extra != NULL){ if (p->extra) { snprintf(comm,4096,"cat %s | %s -s '[Virus] found in a mail to %s' %s", mailx, config->mail, paramlist_get(p->params, "%USERNAME%"),config->extra); do_log(LOG_DEBUG,"cat %s | %s -s '[Virus] found in a mail to %s' %s", mailx, config->mail, paramlist_get(p->params, "%USERNAME%"),config->extra); if(system(comm)) do_log(LOG_CRIT,"ERR: mailx"); do_log(LOG_DEBUG,"Sent p3scan.extra message to %s",config->extra); } else { snprintf(comm,4096,"cat %s | %s -s '[Virus] found in a mail to %s' %s", mail, config->mail, paramlist_get(p->params, "%USERNAME%"),config->extra); do_log(LOG_DEBUG,"cat %s | %s -s '[Virus] found in a mail to %s' %s", mail, config->mail, paramlist_get(p->params, "%USERNAME%"),config->extra); if(system(comm)) do_log(LOG_CRIT,"ERR mail"); do_log(LOG_DEBUG,"Sent copy of message to %s",config->extra); } } unlink(mail); w_free(mail,"mail4"); if (p->extra) { unlink(mailx); w_free(mailx,"mailx4"); } p->ismail=0; if (len>0) return 0; return -1; } struct proxycontext * context_init(void){ struct proxycontext * p; p=w_malloc(sizeof(struct proxycontext)); p->ismail=0; p->msgnum=0; p->mailcount=0; p->bytecount=0; p->socksize=sizeof(struct sockaddr_in); p->client_fd=-1; p->server_fd=-1; p->header_fd=-1; p->hdroffset=0; p->clientbuf=NULL; p->serverbuf=NULL; p->hdrbuf=NULL; p->virinfo=NULL; p->scanthis=NULL; p->scannerinit=SCANNER_INIT_NO; p->usessl=0; p->ssl=NULL; p->ctx=NULL; p->sbio=NULL; p->topping=0; p->posttop=0; p->dspamuser=NULL; p->actsvr=NULL; p->actport=0; return p; } void context_uninit(struct proxycontext * p){ if (p->client_fd > 0 ) close(p->client_fd); if (p->header_fd > 0 ) close(p->header_fd); if (p->server_fd > 0 ){ if(p->usessl) SSL_destroy_conn(p->server_fd, p->ssl, p->ctx, p->sbio); else close(p->server_fd); } paramlist_uninit(&p->params); linebuf_uninit(p->clientbuf); linebuf_uninit(p->serverbuf); if (config->broken) linebuf_uninit(p->hdrbuf); w_free(p,"p"); } void closehdrfile(struct proxycontext * p){ p->fakehdrdone=1; close(p->header_fd); p->hdroffset=0; p->header_fd = open(p->p3shdrfile, O_RDONLY); if (p->header_fd<0){ context_uninit(p); config->emergency=make_message("Critical error opening file '%s', Program aborted.", p->p3shdrfile); do_log(LOG_EMERG,"ERR: Critical error opening file '%s', Program aborted.", p->p3shdrfile); /* should not reach here as we are dead */ } p->now = time(NULL); if (!p->notified && !p->cksmtp){ do_log(LOG_DEBUG, "Informing email client to wait..."); writeline_format(p->client_fd, WRITELINE_LEADING_RN,"+OK P3Scan'ing..."); p->notified=1; } p->ismail=3; } void do_sigterm_proxy(int signr){ do_log(LOG_DEBUG, "do_sigterm_proxy, signal %i", signr); if (global_p == NULL){ do_log(LOG_DEBUG, "already uninitialized"); return; } if (signr != -1) do_log(LOG_CRIT, "ERR: We cot SIGTERM!"); /* sig -1 is ok */ if (global_p->client_fd != -1) close(global_p->client_fd); if (global_p->server_fd != -1) close(global_p->server_fd); if (global_p->scannerinit==SCANNER_INIT_OK && config->scanner->uninit2){ do_log(LOG_DEBUG, "calling uninit2"); config->scanner->uninit2(global_p); do_log(LOG_DEBUG, "uninit2 done"); } do_log(LOG_DEBUG, "Uninit context"); context_uninit(global_p); global_p=NULL; do_log(LOG_DEBUG,"context_uninit done, exiting now"); if (signr != -1) exit(1); } int proxy(struct proxycontext *p){ fd_set fds_read; struct timeval timeout; int scanfd=-1; int error; int maybe_a_space; // signals a space in the keyword for setting USERNAME var int clientret, serverret; unsigned long len, smtpsze; char buf[64]; #ifdef SET_TOS int tos; #endif int scannerret, ret; int trapdata=0, trapcapa1=0, postdata=0; int trapcapa2=0, trapcapa3=0; int smtpcmd=0; int smtprstlb=0; char *smtptr=NULL; char fcmd[512]; FILE * scanner; static char line[512]; int loc,loc2,first=1; char *tmp=NULL; char *tmp2=NULL; p->now = time(NULL); p->header_fd=-1; p->noop=0; p->cksmtp=0; p->usessl=0; p->server_addr.sin_family = AF_INET; /* // For testing: do_log(LOG_DEBUG,"Going to emergency mode..."); config->emergency=make_message("This is a test of the Emergency Termination System: %s", config->runasuser); do_log(LOG_EMERG, "This is a test of the Emergency Termination System: %s", config->runasuser); */ if (config->useurl){ p->clientbuf=linebuf_init(4096); if (writeline(p->client_fd, WRITELINE_LEADING_RN,POK)){ do_log(LOG_CRIT, "ERR: Can't send +OK to client!"); return 1; } while(1){ clientret=getlinep3(p->client_fd, p->clientbuf); if (clientret==GETLINE_OK){ if (!strncasecmp(p->clientbuf->line,"user",4)){ tmp=strchr(p->clientbuf->line,'#'); tmp2=strchr(p->clientbuf->line,':'); if (!tmp && !tmp2) { do_log(LOG_CRIT, "Unable to find destination username#server:port separators %s", substr(p->clientbuf->line,6,strlen(p->clientbuf->line))); return SCANNER_RET_ERR; } if (!tmp) { do_log(LOG_CRIT, "Unable to find destination #server %s",substr(p->clientbuf->line,6,strlen(p->clientbuf->line))); return SCANNER_RET_ERR; } if (!tmp2) { do_log(LOG_CRIT, "Unable to find destination :port %s",substr(p->clientbuf->line,6,strlen(p->clientbuf->line))); return SCANNER_RET_ERR; } loc = tmp-p->clientbuf->line; loc2 = tmp2-p->clientbuf->line; len=strlen(p->clientbuf->line); p->dspamuser=strdup(substr(p->clientbuf->line,5,loc-5)); p->actsvr=strdup(substr(p->clientbuf->line,loc+1,loc2-loc-1)); p->actport=atoi(strdup(substr(p->clientbuf->line,loc2+1,len-loc2))); p->server_addr.sin_port=htons(p->actport); p->server_host=gethostbyname(p->actsvr); if (p->server_host){ memcpy(&p->server_addr.sin_addr.s_addr, p->server_host->h_addr_list[0], p->server_host->h_length); } else { p->server_addr.sin_addr.s_addr = inet_addr(p->actsvr); } p->clientbuf->linelen=GETLINE_LINE_NULL; break; } else { do_log(LOG_DEBUG,"client said: %s",p->clientbuf->line); if (writeline(p->client_fd, WRITELINE_LEADING_RN,"-ERR Not Identified")){ do_log(LOG_CRIT, "ERR: Can't send '-ERR Not Identified' to client!"); return 1; } do_log(LOG_DEBUG,"Told client: -ERR Not Identified"); } } } } else { if (htonl(INADDR_ANY) == config->targetaddr.sin_addr.s_addr) { if (getsockopt(p->client_fd, SOL_IP, SO_ORIGINAL_DST, &p->server_addr, &p->socksize)){ do_log(LOG_CRIT, "ERR: No IP-Conntrack-data (getsockopt failed)"); return 1; } /* try to avoid loop */ if (((ntohl(p->server_addr.sin_addr.s_addr) == INADDR_LOOPBACK) && p->server_addr.sin_port == config->addr.sin_port ) /* src.ip == dst.ip */ || (p->server_addr.sin_addr.s_addr == p->client_addr.sin_addr.s_addr)){ do_log(LOG_CRIT, "ERR: Oops, that would loop!"); return 1; } } else { /* non-proxy mode */ p->server_addr.sin_addr.s_addr = config->targetaddr.sin_addr.s_addr; p->server_addr.sin_port = config->targetaddr.sin_port; } } /* open socket to 'real-server' */ if ((p->server_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0){ do_log(LOG_CRIT, "ERR: Cannot open socket to real-server"); return 1; } #ifdef SET_TOS tos=SET_TOS; if (setsockopt(p->client_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) do_log(LOG_WARNING, "Can't set TOS (incoming connection)"); if (setsockopt(p->server_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) do_log(LOG_WARNING, "Can't set TOS (outgoing connection)"); #endif p->serverbuf=linebuf_init(4096); p->params=paramlist_init(); if (!config->useurl) p->clientbuf=linebuf_init(4096); /* Check connection type */ if (ntohs(p->server_addr.sin_port)==config->smtpport){ p->cksmtp=1; /* Processing an SMTP message */ p->checksmtp=1;/* We should scan it */ do_log(LOG_NOTICE, "SMTP Connection from %s:%i", inet_ntoa(p->client_addr.sin_addr), ntohs(p->client_addr.sin_port)); } else if(ntohs(p->server_addr.sin_port)==config->sslport){ p->usessl=1; do_log(LOG_NOTICE, "POP3S Connection from %s:%i", inet_ntoa(p->client_addr.sin_addr), ntohs(p->client_addr.sin_port)); } else { do_log(LOG_NOTICE, "POP3 Connection from %s:%i", inet_ntoa(p->client_addr.sin_addr), ntohs(p->client_addr.sin_port)); } do_log(LOG_NOTICE, "Real-server address is %s:%i", inet_ntoa(p->server_addr.sin_addr), ntohs(p->server_addr.sin_port)); if (p->usessl) ret=SSL_create_conn(p->server_fd, (struct sockaddr *) &p->server_addr, p->socksize, &p->ssl, &p->ctx, &p->sbio); else ret=connect(p->server_fd, (struct sockaddr *) &p->server_addr, p->socksize); if(ret) { do_log(LOG_CRIT, "ERR: Cannot connect to real-server: %s",inet_ntoa(p->server_addr.sin_addr)); return 1; } set_defaultparams(p); /* releasing sockfd * it seems to work, that if the listener (our parent-PID) gots kicked * we can work AND another listener can bind to the port */ close(sockfd); do_log(LOG_DEBUG, "starting mainloop"); while (1){ /* read from client */ clientret=getlinep3(p->client_fd, p->clientbuf); if (clientret<0){ if (clientret==GETLINE_TOO_LONG){ do_log(LOG_DEBUG, "Line too long: Getting rest of line."); } else { do_log(LOG_DEBUG, "Closing connection (no more input from client)"); return 0; } } if (clientret==GETLINE_OK){ if ( p->cksmtp){ if (p->ismail < 2 || p->ismail >4) do_log(LOG_DEBUG, "--> %s", p->clientbuf->line); else{ #ifdef DEBUG_SMTP do_log(LOG_DEBUG, "--> %s", p->clientbuf->line); #endif } } else do_log(LOG_DEBUG, "--> %s", p->clientbuf->line); } /* read from server */ if (p->usessl) serverret=getline_ssl(p->ssl, p->serverbuf); else serverret=getlinep3(p->server_fd, p->serverbuf); if (serverret<0){ if (serverret==GETLINE_TOO_LONG){ do_log(LOG_DEBUG, "Line too long: Getting rest of line."); } else { do_log(LOG_DEBUG, "Closing connection (no more input from server)"); return 0; } } else { if (p->noop){ if (!strncasecmp(p->serverbuf->line,POK, 3)){ do_log(LOG_DEBUG, "%s: NOOP response. Flushed %i NOOP's", p->serverbuf->line, p->noop); linebuf_zero(p->serverbuf); p->noop=0; } else { do_log(LOG_DEBUG, "Oops, %s doesn't looks like a server's NOOP response. Waiting next...", p->serverbuf->line); } } } if (serverret==GETLINE_OK && p->serverbuf->line != NULL #ifndef DEBUG_MESSAGE && (p->ismail < 2 || p->ismail > 3)// Are we processing a message? #endif ) do_log(LOG_DEBUG, "<-- %s", p->serverbuf->line); if (config->useurl && first){ if (serverret==GETLINE_OK && p->serverbuf->line != NULL && !strncasecmp(p->serverbuf->line,"+OK",3)) { do_log(LOG_DEBUG, "Not sending %s to client...", p->serverbuf->line); p->serverbuf->linelen=GETLINE_LINE_NULL; first=0; paramlist_set(p->params, "%USERNAME%",p->dspamuser); do_log(LOG_DEBUG,"--> USER %s",p->dspamuser); writeline_format(p->server_fd, WRITELINE_LEADING_RN,"USER %s",p->dspamuser); do_log(LOG_DEBUG, "..................us us us us us......................"); do_log(LOG_DEBUG, "............and them them them them them.............."); do_log(LOG_DEBUG, "........and after all, were only ordinary men........."); do_log(LOG_DEBUG, "..................me me me me me......................"); do_log(LOG_DEBUG, "...............and you you you you you................"); do_log(LOG_DEBUG, "god only knows, its not what we would choose, to do..."); } } if (p->cksmtp){ if (trapdata && !smtpcmd && !postdata){ do_log(LOG_DEBUG, "not reading input buffers..."); } else { if (clientret == GETLINE_NEED_READ && serverret == GETLINE_NEED_READ){ FD_ZERO(&fds_read); FD_SET(p->client_fd, &fds_read); FD_SET(p->server_fd, &fds_read); timeout.tv_sec = 300; timeout.tv_usec = 0; if ((ret=select(p->server_fd + 1, &fds_read, NULL, NULL, &timeout))<1){ /* timeout */ do_log(LOG_DEBUG, "select returned %i", ret); break; } else continue ; } } } else { if (clientret == GETLINE_NEED_READ && serverret == GETLINE_NEED_READ){ FD_ZERO(&fds_read); FD_SET(p->client_fd, &fds_read); FD_SET(p->server_fd, &fds_read); timeout.tv_sec = 300; timeout.tv_usec = 0; if ((ret=select(p->server_fd + 1, &fds_read, NULL, NULL, &timeout))<1){ /* timeout */ do_log(LOG_DEBUG, "select returned %i", ret); break; } else continue ; } } if (p->ismail>3) p->serverbuf->line=NULL; if (p->clientbuf->linelen>=0 && p->ismail<2 ){ // Not processing message /* scan command the client sent */ if (!strncasecmp(p->clientbuf->line,"retr", 4)){ p->msgnum=atoi(&p->clientbuf->line[5]); if (p->msgnum<1){ /* that's not ok */ do_log(LOG_WARNING,"RETR msg %i (<1) !!!! ", p->msgnum); p->ismail=0; } else { do_log(LOG_DEBUG,"RETR %i (%d)", p->msgnum, config->scannerenabled); /* enable message parsing (only if scanner enabled) */ if (config->scannerenabled){ p->ismail=1; } p->mailcount++; } } else if (!strncasecmp(p->clientbuf->line,"user",4)){ paramlist_set(p->params, "%USERNAME%",right(p->clientbuf->line,strlen(p->clientbuf->line)-5)); p->dspamuser=paramlist_get(p->params,"%USERNAME%"); } else if (!strncasecmp(p->clientbuf->line,"capa",4)){ trapcapa1=1; trapcapa2=1; trapcapa3=1; do_log(LOG_DEBUG,"Client checking server CAPABILITIES..."); } else if (!strncasecmp(p->clientbuf->line,"top", 3)){ if(config->enabletop) { p->msgnum=atoi(&p->clientbuf->line[4]); if (p->msgnum<1){ // that's not ok do_log(LOG_WARNING,"TOP msg %i (<1) !!!! ", p->msgnum); p->ismail=0; } else { do_log(LOG_DEBUG,"TOP %i", p->msgnum); // enable message parsing (only if scanner enabled) if (config->scannerenabled){ p->ismail=1; p->topping=1; } p->mailcount++; } } else { do_log(LOG_WARNING,"Ignoring client TOP request." ); p->clientbuf->linelen=GETLINE_LINE_NULL; /* don't sent to server */ } } else if (!strncasecmp(p->clientbuf->line,"mail from:", 10) && p->cksmtp){ /* we have size of message */ if (config->smtpsize){ if((smtptr=stristr(p->clientbuf->line,"size="))){ /* we have size of message */ smtpsze=atoi(substr(smtptr,5,strlen(smtptr)-5)) / 1024; if (smtpsze > config->smtpsize){ do_log(LOG_CRIT,"SMTP Message too large for scanning: %i",smtpsze); p->checksmtp=0; p->cksmtp=0; p->ismail=0; close(p->header_fd); unlink(p->p3shdrfile); }else do_log(LOG_DEBUG,"smtpsize=%i",smtpsze); } } if (p->checksmtp) smtpcmd++; } else if (!strncasecmp(p->clientbuf->line,"rcpt to:", 8) && p->cksmtp){ if (p->checksmtp) smtpcmd++; } else if (!strncasecmp(p->clientbuf->line,"data", 4) && p->cksmtp && p->checksmtp){ /* SMTP message being submitted */ if (config->scannerenabled){ p->ismail=1; trapdata=1; /* do not send "DATA" command to server */ do_log(LOG_DEBUG,"intercepted DATA command. smtpcmd=%i",smtpcmd); p->clientbuf->linelen=GETLINE_LINE_NULL; smtprstlb=1; } p->mailcount++; } else { p->ismail=0; } if ((maybe_a_space = !strncasecmp(p->clientbuf->line,"apop", 4)) || !strncasecmp(p->clientbuf->line,"user", 4)){ len=p->clientbuf->linelen -5; if( len >= sizeof(buf)) len=sizeof(buf)-1; if (len>0){ memcpy(buf, (char *)(p->clientbuf->line)+5, len ); buf[len]='\0'; /* we don't want a space (surely with "apop") strtok is another choice. */ if(maybe_a_space){ char *pbuf=strchr(buf,' '); if(NULL != pbuf) *pbuf='\0'; } TRIM(buf); if (strlen(NONULL(paramlist_get(p->params, "%USERNAME%")))) paramlist_set(p->params, "%USERNAME%", buf); } else { if (strlen(NONULL(paramlist_get(p->params, "%USERNAME%")))) paramlist_set(p->params, "%USERNAME%", "unknown"); } do_log(LOG_NOTICE, "USER '%s'", paramlist_get(p->params, "%USERNAME%")); if (strlen(NONULL(p->dspamuser))) p->dspamuser=paramlist_get(p->params,"%USERNAME%"); } /* write clientbuf to server_fd */ if (p->clientbuf->linelen>=0){ if (p->usessl) ret=writeline_ssl(p->ssl, WRITELINE_LEADING_RN, p->clientbuf->line); else ret=writeline(p->server_fd, WRITELINE_LEADING_RN, p->clientbuf->line); if (ret){ do_log(LOG_CRIT, "ERR: Can't send to server!"); return 1; } } p->clientbuf->linelen=GETLINE_LINE_NULL; }// Not processing message if (p->cksmtp && trapdata && !smtpcmd && !postdata){ /* tell the client we will accept their smtp traffic. */ /* It seems RFC2821 says we can reset (abort) a submission by sending RSET after a DATA, EOM event like such: ... DATA ... . RSET but in the real world, this does not seem to work. So, we are going to let the server hang while we are processing this submission. Otherwise, the smtp server will send a partial message to the recipient in the event we want to abort the sending of the message. If we find that we do not wish to send the messge, we can then cleanly abort it. This assumes the actual server would have accepted the data and in my eyes, is not a clean solution. */ if (writeline(p->client_fd, WRITELINE_LEADING_RN,"354 "PROGNAME" "VERSION" accepting data.")){ do_log(LOG_CRIT, "ERR: Can't send 354 to client!"); //context_uninit(p); return 1; } else { do_log(LOG_DEBUG, "Sent 354 "PROGNAME" "VERSION" accepting data."); postdata=1; } } if (p->serverbuf->linelen>=0 || (trapdata && !smtpcmd)){ if ((p->ismail==1 && !p->cksmtp) || (p->ismail==1 && p->cksmtp && !smtpcmd)){ /* scan for answer */ if (!strncasecmp(p->serverbuf->line,"+ok", 3) || trapdata){ /* Set timer now because we might have parsed alot of message numbers */ p->now = time(NULL); /* generate unique filename */ len=strlen(config->virusdir)+14; snprintf(p->mailfile, len, "%sp3scan.XXXXXX", config->virusdir); snprintf(p->p3shdrfile, len, "%sp3shdr.XXXXXX", config->virusdir); if (( scanfd=p3_mkstemp(p->mailfile)) < 0 ){ p->ismail=0; context_uninit(p); config->emergency=make_message("Critical error opening file '%s', Program aborted.", p->mailfile); do_log(LOG_EMERG,"ERR: Critical error opening file '%s', Program aborted.", p->mailfile); /* Should not reach here as we are dead */ } else { p->filename=right(p->mailfile,14); if (( p->header_fd=p3_mkstemp(p->p3shdrfile)) < 0 ){ p->ismail=0; context_uninit(p); config->emergency=make_message("Critical error opening file '%s', Program aborted.", p->p3shdrfile); do_log(LOG_EMERG,"ERR: Critical error opening file '%s', Program aborted.", p->p3shdrfile); /* Should not reach here as we are dead */ } p->ismail=2; p->header_exists=0; p->fakehdrdone=0; p->notified=0; p->gobogus=0; p->boguspos=0; p->hdrdate=0; p->hdrfrom=0; p->hdrto=0; p->errmsg=0; if (!p->cksmtp){ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send response */ if (config->broken) p->hdrbuf=linebuf_init(16384); } } } else { do_log(LOG_DEBUG, "ismail=1, but we haven't got '+ok' so... setting p->ismail=0"); p->ismail=0; } } else if (p->ismail>1){ /* that means that we have to read the mail into file */ if ((p->cksmtp && p->clientbuf->linelen == 0 && !p->header_exists) || (!p->cksmtp && p->serverbuf->linelen == 0 && !p->header_exists)){ if (p->cksmtp){ writeline(scanfd, WRITELINE_LEADING_N, "X-" PROGNAME ": v" VERSION " by Jack S. Lai. No outbound virus detected."); writeline(p->header_fd, WRITELINE_LEADING_N, "X-" PROGNAME ": v" VERSION " by Jack S. Lai. No outbound virus detected."); if(config->footer!=NULL){ len=strlen(config->footer); snprintf(fcmd, len+1, "%s 2>&1", config->footer); if ((scanner=popen(fcmd, "r"))==NULL){ do_log(LOG_CRIT, "ERR: Can't get scanner version '%s' !!!", fcmd); }else{ while ((fgets(line, 512, scanner))!=NULL){ if (line[0] != '\r' && line[0] != '\n'){ line[strlen(line)-1]='\0'; writeline_format(scanfd, WRITELINE_LEADING_N,"X-" PROGNAME ": %s", line); writeline_format(p->header_fd, WRITELINE_LEADING_N,"X-" PROGNAME ": %s", line); } } pclose(scanner); } } } else { writeline(scanfd, WRITELINE_LEADING_N, "X-" PROGNAME ": Version " VERSION " by /"); writeline(p->header_fd, WRITELINE_LEADING_N, "X-" PROGNAME ": Version " VERSION " by /"); } p->header_exists=1; /* This is the first response the client gets after "RETR/TOP", so start countdown timer now... */ if (p->ismail < 3){ do_log(LOG_DEBUG,"Closing header buffer."); closehdrfile(p); }else if (!p->cksmtp) do_log(LOG_DEBUG,"notified=%i",p->notified); } if (p->cksmtp){ if(!strncasecmp(p->clientbuf->line,"Date:",5) && p->ismail < 3) p->hdrdate=1; if(!strncasecmp(p->clientbuf->line,"From:",5) && p->ismail < 3) p->hdrfrom=1; if(!strncasecmp(p->clientbuf->line,"To:",3) && p->ismail < 3) p->hdrto=1; if(strstr(p->clientbuf->line,"MIME") || strstr(p->clientbuf->line,"Content-Type") || !strncasecmp(p->clientbuf->line,"Subject:",8)){ if (p->ismail < 3){ do_log(LOG_DEBUG,"Caught MIME/Subj line, closing header buffer."); closehdrfile(p); } } if (clientret==GETLINE_TOO_LONG){ if (p->clientbuf->linelen >=0) { writeline(scanfd, WRITELINE_LEADING_NONE, p->clientbuf->line); if (p->ismail < 3) writeline(p->header_fd, WRITELINE_LEADING_NONE, p->clientbuf->line); } } else { if(p->clientbuf->linelen >=0){ writeline(scanfd, WRITELINE_LEADING_N, p->clientbuf->line); if (p->ismail < 3) writeline(p->header_fd, WRITELINE_LEADING_N, p->clientbuf->line); } } } else { if(!strncasecmp(p->serverbuf->line,"Date:",5) && p->ismail < 3) p->hdrdate=1; if(!strncasecmp(p->serverbuf->line,"From:",5) && p->ismail < 3) p->hdrfrom=1; if(!strncasecmp(p->serverbuf->line,"To:",3) && p->ismail < 3) p->hdrto=1; if(strstr(p->serverbuf->line,"MIME") || strstr(p->serverbuf->line,"Content-Type") || !strncasecmp(p->serverbuf->line,"Subject:",8)){ if (((config->scanner->name="bash")) && !strncasecmp(p->serverbuf->line,"Subject:",8)) p->serverbuf->line=strreplace(p->serverbuf->line,"'"," "); if (p->ismail < 3){ do_log(LOG_DEBUG,"Caught MIME/Subj line, closing header buffer."); closehdrfile(p); } } if (serverret==GETLINE_TOO_LONG){ writeline(scanfd, WRITELINE_LEADING_NONE, p->serverbuf->line); if (p->ismail < 3) writeline(p->header_fd, WRITELINE_LEADING_NONE, p->serverbuf->line); } else { writeline(scanfd, WRITELINE_LEADING_N, p->serverbuf->line); if (p->ismail < 3) writeline(p->header_fd, WRITELINE_LEADING_N, p->serverbuf->line); } /* check if isp already marked as spam so we don't tie up anti-spam resources (Read as "SLOW Perl SpamAssassin!" :) For example cox.net marks spam as "-- Spam --". */ if (config->ispspam != NULL && strstr(p->serverbuf->line,config->ispspam)!=NULL) config->ispam=1; /* Here is where we start feeding the client part of our header buffer until the message has been processed */ error=checktimeout(p); if (error < 0) do_log(LOG_CRIT,"ERR: Writing to client!"); } if ((p->clientbuf->linelen==1 && p->clientbuf->line[0]=='.') || (p->serverbuf->linelen==1 && p->serverbuf->line[0]=='.')){ /* mail is complete */ error=0; close(scanfd); do_log(LOG_DEBUG, "got '.\\r\\n', mail is complete."); if (p->ismail==2) closehdrfile(p); p->ismail=4; /* initialize the scanner before scanning the first mail but only if scanning is enabled */ if (config->scannerenabled && p->scannerinit == SCANNER_INIT_NO){ if (config->scanner->init2){ if (config->scanner->init2(p)!=0){ context_uninit(p); config->emergency="Can't initialize scanner"; do_log(LOG_EMERG, "ERR: Can't initialize scanner!"); /* Dead now. Configuration error! */ p->scannerinit=SCANNER_INIT_ERR; }else p->scannerinit=SCANNER_INIT_OK; }else p->scannerinit=SCANNER_INIT_NULL; } set_maildateparam(p->params); set_paramsfrommailheader(p->mailfile, p->params,p); /* Scan the file now */ scannerret=SCANNER_RET_OK; snprintf(p->maildir, 4090, "%s.dir", p->mailfile); if (p->scannerinit > 0){ if ((scannerret=scan_mailfile(p))==SCANNER_RET_VIRUS){ /* virus */ if (p->virinfo) TRIM(p->virinfo); paramlist_set(p->params, "%VIRUSNAME%", NONULL(p->virinfo)); paramlist_set(p->params, "%MAILFILE%", p->mailfile); if (config->delit) paramlist_set(p->params, "%P3SCANID%", config->notify); else paramlist_set(p->params, "%P3SCANID%", p->filename); do_log(LOG_WARNING, "%s from %s:%s to %s:%s from %s to %s user: %s virus: %s file: %s", paramlist_get(p->params,"%PROTOCOL%"), paramlist_get(p->params,"%CLIENTIP%"), paramlist_get(p->params,"%CLIENTPORT%"), paramlist_get(p->params,"%SERVERIP%"), paramlist_get(p->params,"%SERVERPORT%"), paramlist_get(p->params,"%MAILFROM%"), paramlist_get(p->params,"%MAILTO%"), paramlist_get(p->params,"%USERNAME%"), paramlist_get(p->params,"%VIRUSNAME%"), paramlist_get(p->params,"%P3SCANID%") ); if (do_virusaction(p)!=0){ if (p->cksmtp) { /* Try cleaning it up again */ do_log(LOG_CRIT,"ERR: Virusaction failed. Sending 554 and reseting smtp data sent."); writeline_format(p->client_fd, WRITELINE_LEADING_RN, "554 %s",config->smtprset); do_log(LOG_DEBUG,"Sending RSET to real smtp server."); writeline_format(p->server_fd, WRITELINE_LEADING_RN, "RSET"); writeline_format(p->server_fd, WRITELINE_LEADING_RN, "QUIT"); } else { do_log(LOG_CRIT,"ERR: Virusaction failed. Sending -ERR and closing pop3 connection."); writeline_format(p->client_fd, WRITELINE_LEADING_RN,"-ERR Message %i contains a virus (%s).", p->msgnum, paramlist_get(p->params, "%VIRUSNAME%")); } p->ismail=0; return 1; }; unset_paramsfrommailheader(p->params); p->clientbuf->linelen=GETLINE_LINE_NULL; p->serverbuf->linelen=GETLINE_LINE_NULL; if (config->delit) unlink(p->mailfile); } /* virus */ /* see if there was a critical error */ if (scannerret==SCANNER_RET_CRIT){ if (!p->errmsg) do_log(LOG_CRIT,"ERR: Writing to client!"); /* exact error already reported so kill the child. This should get the sysadmins attention. */ p->ismail=0; return 1; } }else scannerret=SCANNER_RET_ERR; /* ! error */ if (scannerret!=SCANNER_RET_VIRUS){ /* send mail if no virus */ if (scannerret==SCANNER_RET_ERR){ do_log(LOG_ALERT, "ERR: We can't say if it is a virus! So we have to give the client the mail! You should check your configuration/system"); context_uninit(p); config->emergency="Scanner returned unexpected error code. You should check your configuration/system."; do_log(LOG_EMERG, "ERR: Scanner returned unexpected error code. You should check your configuration/system."); /* We are dead now. Don't let virus mails pass */ } /* no virus / error / scanning disabled */ do_log(LOG_DEBUG, "Scanning done, sending mail now."); p->ismail=5; if (p->cksmtp){ do_log(LOG_DEBUG, "Sending DATA to server."); if (writeline(p->server_fd, WRITELINE_LEADING_RN, "DATA")){ do_log(LOG_CRIT, "ERR: Can't send DATA to server!"); return 1; } p->serverbuf->linelen=GETLINE_LINE_NULL; /* assume 354 from server */ do_log(LOG_DEBUG, "Sending smtp message to server."); if ((len=send_mailfile(p->mailfile, p->server_fd, p))<0){ do_log(LOG_CRIT, "ERR: Can't submit mail! We have to quit now!"); p->ismail=0; return 1; } } else { if ((len=send_mailfile(p->mailfile, p->client_fd, p))<0){ do_log(LOG_CRIT, "ERR: Can't send mail! We have to quit now!"); p->ismail=0; return 1; } } do_log(LOG_DEBUG, "Sending done."); p->ismail=0; p->bytecount+=len; p->serverbuf->linelen=GETLINE_LINE_NULL; linebuf_zero(p->serverbuf); trapdata=0; unlink(p->mailfile); /* we do not unlink virusmails, so only here */ } close(p->header_fd); unlink(p->p3shdrfile); do_log(LOG_DEBUG, "Mail action complete"); if (config->enabletop){ if (p->topping) p->posttop=1; } } /* mail complete */ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send to client */ } else if (trapcapa1 && !strncasecmp(p->serverbuf->line,"PIPELINING",10)){ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send to client */ trapcapa1=0; do_log(LOG_WARNING, "Ignoring servers PIPELINING capability..."); } else if (!config->enabletop && trapcapa2 && !strncasecmp(p->serverbuf->line,"TOP",3)){ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send to client */ trapcapa2=0; do_log(LOG_WARNING, "Ignoring servers TOP capability..."); } else if (trapcapa3 && !strncasecmp(p->serverbuf->line,"STLS",4)){ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send to client */ trapcapa3=0; do_log(LOG_WARNING, "Ignoring servers STLS capability..."); } else if (p->cksmtp && !strncasecmp(p->serverbuf->line,"250-PIPELINING",14)){ p->serverbuf->linelen=GETLINE_LINE_NULL; /* don't send to client */ do_log(LOG_WARNING, "Ignoring SMTP servers PIPELINING capability..."); } } /* server_buf_len >0 */ /* we are not in mail-reading mode (ismail==0) */ if ( p->serverbuf->linelen>=0 ){ /* write server_buf to fd */ if (smtpcmd && !strncasecmp(p->serverbuf->line,"250", 3)) smtpcmd--; if (!p->cksmtp || (p->cksmtp && strncasecmp(p->serverbuf->line,"354", 3))){ if (writeline(p->client_fd, WRITELINE_LEADING_RN, p->serverbuf->line)){ do_log(LOG_CRIT, "ERR: Can't send to client"); return 1; } } else if (p->cksmtp) do_log(LOG_DEBUG,"Caught servers 354"); p->serverbuf->linelen=GETLINE_LINE_NULL; p->clientbuf->linelen=GETLINE_LINE_NULL; } } //end of while do_log(LOG_WARNING, "Connection timeout"); do_log(LOG_DEBUG, "Child finished"); return 0; } int do_sigchld_check(pid_t pid, int stat){ int termsig = WTERMSIG(stat); if (numprocs>0) numprocs--; if (termsig){ do_log(LOG_CRIT, "ERR: Attention: child with pid %i died with abnormal termsignal (%i)! This is probably a bug. Please report to the author. numprocs is now %i", pid, termsig, numprocs); }else{ do_log(LOG_DEBUG, "waitpid: child %i died with status %i, numprocs is now %i", pid, WEXITSTATUS(stat), numprocs); } if(clean_child_directory(pid)) do_log(LOG_DEBUG, "Error cleaning child directory!"); return 1; } void do_sigchld(int signr){ pid_t pid; int stat; while ((pid=waitpid(-1, &stat, WNOHANG)) > 0){ do_sigchld_check(pid, stat); } } void printversion(void){ printf( "\n" PROGNAME " " VERSION "\n" "(C) 2003-2005 by Jack S. Lai, 12/05/2003\n" " and by Folke Ashberg \n" "\n" ); } /* TODO: Enable ONLY -d -f -h and -v as command line options. */ void usage(char * progname){ int i=0; printversion(); printf( "Usage: %s [options]\n" "\n" "where options are:\n" " -a, --renattach=FILE Specify location of renattach if wanted\n" " -A, --altvnmsg Creates a copy of 'template=FILE' for manipulation\n" " prior to use. /var/spool/p3scan/children//vnmsg\n" " -b, --bytesfree=NUM Number (in KBytes) that should be available before we\n" " can process messages. If not enough, report it and die.\n" " -B, --broken Enable broken processing (Outlook/Outlook Express clients).\n" " -c, --viruscode=N[,N] The code(s) the scanner returns when a virus is found\n" " -C, --checksize=NUM Number (in KBytes) of the maximum smtp message size.\n" " -d, --debug Turn on debugging. See " CONFIGFILE " for recommended\n" " debug procedure.\n" " -e, --extra=ADDR Extra notification of recipient's email address\n" " -E, --emailport=PORT Port to check smtp (email) submissions on\n" " -f, --configfile=FILE Specify a configfile\n" " Default is " CONFIGFILE "\n" " -F, --footer=CMD Specify a command to get the version info of your scanner\n" " if using the smtp footer feature file" FOOTER "\n" " -g, --virusregexp=RX Specify a RegularExpression which describes where to\n" " get the name of the virus. The first substring is\n" " used, or if regexp ends with /X the X substring\n" " -G --goodcode The codes that enable the message to be delivered without a\n" " warning. For example Kaspersky AV reports code 10 for an\n" " encrypted .zip file\n" " -h, --help Prints this text\n" " -i, --ip=IP Listen only on IP . Default: ANY\n" " -I, --targetip=IP Connect only to IP . Default: use transparent-proxy\n" " -j, --justdelete Just delete infected mail after reporting infection\n" " -J, --enabletop Allow possible broken TOP command processing\n" " -k, --checkspam Turn on Spam Checking\n" " -K, --emergcon=ADDR Emergency Contact email address to be notified in event\n" " of program termination like no disk space.\n" " -l, --pidfile=FILE Specify where to write a pid-file\n" " -L, --sslport=PORT Use SSL on connections to port . Default %i\n" " -m, --maxchilds=NUM Allow not more then NUM childs\n" " -M, --ispspam Specify a line used by your ISP to mark Spam\n" " For example, cox.net uses -- Spam --\n" " -n, --notifydir=DIR Create notification mails in \n" " Default: " NOTIFY_MAIL_DIR "\n" " Also used for temporary storing\n" " -N, --notify Change infected file status line\n" " -o, --overwrite Specify path to HTML parsing program executable.\n" " Default none\n" " -O, --timeout=NUM Specify seconds to use for timeout notificatino.\n" " -p, --port=PORT Listen on port . Default: %i\n" " -P, --targetport=PORT Connect to port . Default: %i\n" " Ignored in transparent proxy mode\n" " -q, --quiet Turn off normal reporting\n" " -r, --virusdir=DIR Save infected mails in \n" " Default: " VIRUS_DIR "\n" " -R, --smtprset Change smtp reject message line\n" " -s, --scanner=FILE Specify the scanner. Every scannertype handles this\n" " in an other way. This could be the scanner-\n" " executable or a FIFO, Socket, ...\n" " -S, --subject=TEXT Change virus reporting subject line\n" " -t, --template=FILE Use virus-notification-template \n" , basename(progname), POP3SPORT, PORT_NUMBER, PORT_NUMBER); printf( " -T, --scannertype=T Define which buildin scanner-frontend to use.\n\n" " Supported types:\n"); while (scannerlist[i]){ printf("%17s: %s\n", scannerlist[i]->name, scannerlist[i]->descr); i++; } printf( "\n" " -u, --user=[UID|NAME] Run as user . Default: " RUNAS_USER "\n" " Only takes effect when started as superuser\n" " -U, --useurl Parse username for destination url instead of\n" " iptables redirect\n" " -v, --version Prints version information\n" " -x, --demime eXtract all MIME-Parts before scanning\n" " -X, --xmail=FILE Xtra notification rcpt mail pgm. Default: " XMAIL "\n" " -z, --spamcheck=FILE Specify path to Spam Checking program executable\n" " Default /usr/bin/spamc (Mail::SpamAssassin)\n" "\n" " see " CONFIGFILE " for detailed information\n" "\n" ); } void parseoptions(int argc, char **argv){ long i, ii; char * rest; int error = 0; struct servent *port; int fp, res; struct linebuf *cf; char *pargv[MAX_PSEUDO_ARGV]; int pargc; char *line; int pidfd; int dofree=0; struct option long_options[] = { { "renattach", required_argument, NULL, 'a' }, { "altvnmsg", no_argument, NULL, 'A' }, { "bytesfree", required_argument, NULL, 'b' }, { "broken", no_argument, NULL, 'B' }, { "viruscode", required_argument, NULL, 'c' }, { "checksize", required_argument, NULL, 'C' }, { "debug", no_argument, NULL, 'd' }, { "extra", required_argument, NULL, 'e' }, { "emailport", required_argument, NULL, 'E' }, { "configfile", required_argument, NULL, 'f' }, { "footer", required_argument, NULL, 'F' }, { "virusregexp", required_argument, NULL, 'g' }, { "goodcode", required_argument, NULL, 'G' }, { "help", no_argument, NULL, 'h' }, { "ip", required_argument, NULL, 'i' }, { "targetip", required_argument, NULL, 'I' }, { "justdelete", no_argument, NULL, 'j' }, { "enabletop", no_argument, NULL, 'J' }, { "checkspam", no_argument, NULL, 'k' }, { "emergcon", required_argument, NULL, 'K' }, { "pidfile", required_argument, NULL, 'l' }, { "sslport", required_argument, NULL, 'L' }, { "maxchilds", required_argument, NULL, 'm' }, { "ispspam", required_argument, NULL, 'M' }, { "notifydir", required_argument, NULL, 'n' }, { "notify", required_argument, NULL, 'N' }, { "overwrite", required_argument, NULL, 'o' }, { "timeout", required_argument, NULL, 'O' }, { "port", required_argument, NULL, 'p' }, { "targetport", required_argument, NULL, 'P' }, { "quiet", no_argument, NULL, 'q' }, { "virusdir", required_argument, NULL, 'r' }, { "smtprset", required_argument, NULL, 'R' }, { "scanner", required_argument, NULL, 's' }, { "subject", required_argument, NULL, 'S' }, { "template", required_argument, NULL, 't' }, { "scannertype", required_argument, NULL, 'T' }, { "user", required_argument, NULL, 'u' }, { "useurl", no_argument, NULL, 'U' }, { "version", no_argument, NULL, 'v' }, #ifdef DEMIME { "demime", no_argument, NULL, 'x' }, #endif { "xmail", required_argument, NULL, 'X' }, { "spamcheck", required_argument, NULL, 'z' }, { NULL, no_argument, NULL, 0 } }; #ifdef DEMIME char getoptparam[] = "hvf:a:Ab:Bc:C:de:F:g:G:i:I:jJkK:l:L:m:M:n:N:o:O:p:P:qr:R:s:S:t:T:u:UxX:z:"; #else char getoptparam[] = "hvf:a:Ab:Bc:C:de:F:g:G:i:I:jJkK:l:L:m:M:n:N:o:O:p:P:qr:R:s:S:t:T:u:UX:z:"; #endif void switchoption(char opt, char * arg, char * optstr, char * where, int state){ char *next_tok; switch (opt){ case 'h': case 'v': case 'f': /* don't check in second run (is in the first) */ if (state==CONFIG_STATE_CMD) return; /* disallow help/version/configfile in configfile */ if (state==CONFIG_STATE_FILE){ fprintf(stderr, "%s '%s' is not allowed in configfile!\n", where, optstr); error=1; return; } break; default: /* only check help/version/configfile for the first cmd run */ if (state==CONFIG_STATE_CMDPRE) return; } switch (opt){ case 'h': /* usage */ usage(argv[0]); exit(0); break; case 'v': /* version */ printversion(); exit(0); break; case 'f': /* config (file) */ config->configfile = arg; break; case 'F': /* footer (file) */ config->footer = arg; break; case 'd': /* debug */ config->debug=1; break; case 'e': /* Extra notification */ config->extra=arg; break; case 'E': /* SMTP (email) port */ i=strtol(arg, &rest, 10); if (rest && strlen(rest)>0){ if (i>0){ /* 123abc */ fprintf(stderr, "%s %s isn't a valid port\n", where, arg); error=1; }else{ if((port=getservbyname(arg, "tcp"))!=NULL) config->smtpport=ntohs(port->s_port); else{ fprintf(stderr, "Port lookup for '%s/tcp' failed! Check /etc/services\n", arg); error=1; } } } else { if (i>0) config->smtpport=i; else { fprintf(stderr, "%s Incorrect emailport portnumber\n", where); error=1; } } break; case 'l': /* PID File */ config->pidfile=arg; if ((pidfd=open(config->pidfile,O_RDONLY ))>=0){ do_log(LOG_EMERG, "ERR: PID file %s exists! Aborting!",config->pidfile); /* Should not reach here. We are dead. */ pidfd=close(pidfd); exit(0); } break; case 'L': /* SSL port */ i=strtol(arg, &rest, 10); if (rest && strlen(rest)>0){ if (i>0){ /* 123abc */ fprintf(stderr, "%s %s isn't a valid port\n", where, arg); error=1; }else{ if((port=getservbyname(arg, "tcp"))!=NULL) config->sslport=ntohs(port->s_port); else{ fprintf(stderr, "Port lookup for '%s/tcp' failed! Check /etc/services\n", arg); error=1; } } } else { if (i>0) config->sslport=i; else { fprintf(stderr, "%s Incorrect POP3S portnumber\n", where); error=1; } } break; case 'a': /* rename attachments using renattach */ config->renattach=arg; break; case 'A': /* use alternate virus notification email */ config->altemail=1; break; case 'r': /* virusdir */ config->virusdirbase=arg; config->virusdir=config->virusdirbase; break; case 'R': /* smtp reject */ config->smtprset=arg; break; case 'n': /* notifydir */ config->notifydir=arg; break; case 'm': /* Max Childs */ i=strtol(arg, &rest, 10); if ((rest && strlen(rest)>0) || i<1 || i>9999){ fprintf(stderr, "%s --maxchilds has to be 1 < val < 10000\n", where); error=1; } else config->maxchilds=(int)i; break; case 'i': /* IP (to listen on) */ if (!strcmp(arg, "0.0.0.0")){ config->addr.sin_addr.s_addr=htonl(INADDR_ANY); }else if (!inet_aton(arg, &config->addr.sin_addr)){ fprintf(stderr, "%s %s isn't a valid IP Adress\n", where, arg); error=1; } break; case 'I': /* IP (to connect) */ if (!strcmp(arg, "0.0.0.0")){ config->targetaddr.sin_addr.s_addr=htonl(INADDR_ANY); }else if (!inet_aton(arg, &config->targetaddr.sin_addr)){ fprintf(stderr, "%s %s isn't a valid IP Adress\n", where, arg); error=1; } break; case 'o': /* overwrite (disable) HTML */ config->overwrite=arg; break; case 'O': /* timeOut */ i=strtol(arg, &rest, 10); if ((rest && strlen(rest)>0) || i<1 || i>9999){ fprintf(stderr, "%s --timeout has to be 1 < val < 10000\n", where); error=1; } else config->timeout=(int)i; break; case 'p': /* Port */ i=strtol(arg, &rest, 10); if (rest && strlen(rest)>0){ if (i>0){ /* 123abc */ fprintf(stderr, "%s %s isn't a valid port\n", where, arg); error=1; }else{ if((port=getservbyname(arg, "tcp"))!=NULL){ config->addr.sin_port=port->s_port; }else{ fprintf(stderr, "Port lookup for '%s/tcp' failed! Check /etc/services\n", arg); error=1; } } }else{ if (i>0)config->addr.sin_port=htons((int)i); else{ fprintf(stderr, "%s Incorrect POP3 portnumber\n", where); error=1; } } break; case 'P': /* target Port */ i=strtol(arg, &rest, 10); if (rest && strlen(rest)>0){ if (i>0){ /* 123abc */ fprintf(stderr, "%s %s isn't a valid port\n", where, arg); error=1; }else{ if((port=getservbyname(arg, "tcp"))!=NULL){ config->targetaddr.sin_port=port->s_port; }else{ fprintf(stderr, "Port lookup for '%s/tcp' failed! Check /etc/services\n", arg); error=1; } } }else{ if (i>0)config->targetaddr.sin_port=htons((int)i); else{ fprintf(stderr, "%s Incorrect target portnumber\n", where); error=1; } } break; case 'q': /* quiet */ config->quiet=1; break; case 'u': /* Run as User */ config->runasuser=arg; /* getpwnam will also accept UID's, so we need no converting*/ break; case 'U': /* Parse username for destination url */ config->useurl=1; break; case 's': /* Scanner */ config->virusscanner=arg; break; case 't': /* template */ config->virustemplate=arg; break; case 'c': /* Virus (exit) code */ ii = 0; next_tok = strtok(arg, " \t,"); if (next_tok){ do{ if (ii < MAX_VIRUS_CODES){ i=strtol(next_tok, &rest, 10); if ( (rest && strlen(rest)>0) || i<1 || i>256){ fprintf(stderr, "%s --viruscode has be a list of numbers (%s)\n", where, rest); error=1; }else config->viruscode[ii]=(int)i; ii++; } }while ((next_tok = strtok(NULL, " \t,")) || (ii >= MAX_VIRUS_CODES)); } config->viruscode[ii] = -1; if (ii == 0){ fprintf(stderr, "%s --viruscode has be a list of numbers (%s)\n", where, rest); error=1; } break; case 'G': /* Good Virus (exit) code */ ii = 0; next_tok = strtok(arg, " \t,"); if (next_tok){ do{ if (ii < MAX_VIRUS_CODES){ i=strtol(next_tok, &rest, 10); if ( (rest && strlen(rest)>0) || i<1 || i>256){ fprintf(stderr, "%s --good viruscode has be a list of numbers (%s)\n", where, rest); error=1; }else config->gvcode[ii]=(int)i; ii++; } }while ((next_tok = strtok(NULL, " \t,")) || (ii >= MAX_VIRUS_CODES)); } config->gvcode[ii] = -1; if (ii == 0){ fprintf(stderr, "%s --good viruscode has be a list of numbers (%s)\n", where, rest); error=1; } break; #ifdef DEMIME case 'x': /* demime */ config->demime = 1; break; #endif case 'X': /* Xtra notification reciept mail program */ config->mail=arg; break; case 'T': /* scannertype */ i=0; while (scannerlist[i]){ if(!strcasecmp(arg, scannerlist[i]->name)){ config->scanner=scannerlist[i]; i=-1; break; } i++; } if (i!=-1){ fprintf(stderr, "%s scannertype '%s' is not supported", where, arg); error=1; } break; case 'g': /* virusregexp */ config->virusregexp = arg; i=strlen(arg); if (arg[i-2]=='/' && isdigit(arg[i-1])){ arg[i-2]='\0'; config->virusregexpsub=arg[i-1]-'0'; } break; case 'k': /* check for spam */ config->checkspam=1; break; case 'K': /* emergency Kontact! */ config->emergcon = arg; break; case 'z': /* path to spam checking executable */ config->spamcheck = arg; break; case 'b': /* bytes free */ i=strtol(arg, &rest, 10); config->freespace=(int)i; break; case 'C': /* Check SMTP size */ i=strtol(arg, &rest, 10); config->smtpsize=(int)i; break; case 'j': /* justdelete */ config->delit=1; break; case 'J': /* enabletop */ config->enabletop=1; break; case 'B': /* broken */ config->broken=1; break; case 'S': /* Subject line for virus notification */ config->subject = arg; break; case 'N': /* deleted file notification */ config->notify = arg; break; case 'M': /* ISP marked as SPAM */ config->ispspam = arg; break; default: fprintf(stderr, "%s Option '%s' isn't known\n", where, optstr); error=1; } }/* sub function switchoption }}} */ void parseargs(int c, char **v, char * where, int state){ int option, option_index = 0; opterr=0; optind=1; while (1){ option = getopt_long(c, v, getoptparam, long_options, &option_index); if (option == EOF) break; switchoption(option, optarg, v[optind-1], where, state); } if (state != CONFIG_STATE_CMDPRE && optind < c){ error=1; while (optind < c) fprintf(stderr, "%s Unknown option '%s'\n", where, v[optind++]); } } config=w_malloc(sizeof(struct configuration_t)); /* set defaults for in, char* to NULL */ config->debug=DEBUG; config->quiet=QUIET; config->overwrite=OVERWRITE; config->renattach=NULL; config->addr.sin_port=htons((int)PORT_NUMBER); config->targetaddr.sin_port=htons((int)PORT_NUMBER); config->smtpport=SMTP_PORT; config->maxchilds=MAX_CHILDS; config->viruscode[0]=VIRUS_SCANNER_VIRUSCODE; config->viruscode[1]=-1; config->gvcode[0]=0; config->runasuser=NULL; config->virusdir=NULL; config->virusdirbase=NULL; config->notifydir=NULL; config->virustemplate=NULL; config->virusscanner=NULL; config->demime=0; config->pidfile=NULL; config->sslport=POP3SPORT; config->syslogname=NULL; config->addr.sin_addr.s_addr=htonl(INADDR_ANY); config->targetaddr.sin_addr.s_addr=htonl(INADDR_ANY); config->scanner=NULL; config->virusregexp=NULL; config->virusregexpsub=1; config->checkspam=CHECKSPAM; config->spamcheck=SPAMCHECK; config->freespace=MINSPACE; config->delit=DELIT; config->broken=0; config->ispspam=NULL; config->extra=NULL; config->smtprset=SMTPRSET; config->smtpsize=0; config->mail=XMAIL; config->timeout=TIMEOUT; config->footer=NULL; config->enabletop=0; config->emergcon=NULL; config->emergency=NULL; /* parse line args, but for the first time only configfile/version/help */ parseargs(argc, argv, "\t[cmdlineparm]", CONFIG_STATE_CMDPRE); /* parse configfile */ if (!config->configfile){ config->configfile=strdup(CONFIGFILE); //TODO: 24 bytes in 1 blocks are definitely lost in loss record 2 of 2 dofree=1; } if ((fp=open(config->configfile, O_RDONLY))>=0){ cf=linebuf_init(4096); pargc=1; pargv[0]=""; while ((res=getlinep3(fp, cf))>=0 && pargclinelen > 2){ TRIM(cf->line); if (cf->line[0]!='#' && cf->line[0]!=';' && !(cf->line[0]=='/' && cf->line[1]=='/') && cf->line[0]!='='){ /* copy to pseudo argv, change * 'x = y' or 'x y' to 'x=' * This code is the horror, but it seems to work */ line=w_malloc(strlen(cf->line)+3); line[0]='-'; line[1]='-'; line[2]='\0'; strcat(line, cf->line); pargv[pargc]=line; if ((i=strcspn(line, " =\t"))>1){ if (i=0){ rest=line+i+ii+1; if (rest && strlen(rest)>0 ){ pargv[pargc][strlen(pargv[pargc])]='='; memcpy(pargv[pargc]+i+1, rest, strlen(rest)+1); //TODO: Source and destination overlap in memcpy } } } } pargc++; } } } close(fp); linebuf_uninit(cf); pargv[pargc]=NULL; parseargs(pargc, pargv, "\t[configfile]", CONFIG_STATE_FILE); } if(dofree) free(config->configfile); /* now check the rest of commandline args (higher precedence than configfile) */ parseargs(argc, argv, "\t[cmdlineparm]", CONFIG_STATE_CMD); if (error){ printf( "Commandline options/configfile are not ok\n" "try --help or RTFM to get some information\n" "\n" ); exit(1); } /* set unset values to default */ SETIFNULL(config->runasuser, RUNAS_USER); SETIFNULL(config->virusdirbase, VIRUS_DIR); SETIFNULL(config->virusdir, config->virusdirbase ); SETIFNULL(config->notifydir, NOTIFY_MAIL_DIR); SETIFNULL(config->virustemplate, VIRUS_TEMPLATE); SETIFNULL(config->virusscanner, VIRUS_SCANNER); SETIFNULL(config->pidfile, PID_FILE); SETIFNULL(config->syslogname, SYSLOG_NAME); SETIFNULL(config->scanner, scannerlist[0]); SETIFNULL(config->subject, SUBJECT); SETIFNULL(config->notify, NOTIFY); SETIFNULL(config->smtprset, SMTPRSET); SETIFNULL(config->mail, XMAIL); SETIFNULL(config->emergcon, EMERGCON); } void do_sigterm_main(int signr){ int ret; if (signr != -1 ) do_log(LOG_NOTICE, "signalled, doing cleanup"); close(sockfd); if (config->scannerenabled && config->scanner->uninit1){ do_log(LOG_DEBUG, "calling uninit1"); config->scanner->uninit1(); do_log(LOG_DEBUG, "uninit1 done"); } if((ret=unlink(config->pidfile)!=0)) do_log(LOG_NOTICE, "ERR: Unable to remove %s", config->pidfile); do_log(LOG_NOTICE, PROGNAME " terminates now"); exit(0); } Sigfunc * p3signal(int signo, Sigfunc *func){ struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo != SIGALRM) act.sa_flags |= SA_RESTART; if(sigaction(signo, &act, &oact)<0) return(SIG_ERR); return(oact.sa_handler); } int main(int argc, char ** argv){ int connfd=0, i=0, cuid=0; int abortfd=0; struct sockaddr_in addr; size_t socksize = sizeof(struct sockaddr_in); pid_t pid; int stat=0; FILE * fp; struct passwd *pws; struct proxycontext * p; char * responsemsg; int virusdirlen=0; char chownit[100]; #define CHOWNCMD "/bin/chown" int len=0; int ret=0; FILE * chowncmd; unsigned long kbfree; struct statvfs fs; w_memory_init(); // We need to initialize our memory allocation routines parseoptions(argc, argv); do_log(LOG_NOTICE, PROGNAME " Version " VERSION); do_log(LOG_NOTICE, "Selected scannertype: %s (%s)", config->scanner->name,config->scanner->descr); if((sockfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) do_log(LOG_EMERG, "ERR: Can't open socket!"); i = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); config->addr.sin_family = AF_INET; if (bind(sockfd, (struct sockaddr *) &config->addr, sizeof(config->addr))){ do_log(LOG_EMERG, "ERR: Can't bind to socket %s:%i",inet_ntoa(config->addr.sin_addr), ntohs(config->addr.sin_port)); } if (listen(sockfd, 5)) do_log(LOG_EMERG, "ERR: Can't listen on socket"); do_log(LOG_NOTICE, "Listen now on %s:%i", inet_ntoa(config->addr.sin_addr), ntohs(config->addr.sin_port)); if (!config->debug){ /* daemonize */ if ((pid = fork())<0) return(-1); else if(pid != 0) exit(0); setsid(); if(chdir("/")) do_log(LOG_CRIT,"ERR: chdir"); umask(0); } if ((fp=fopen(config->pidfile, "w+"))!=NULL){ fprintf(fp, "%i\n", getpid()); fclose(fp); }else do_log(LOG_CRIT, "ERR: Can't write PID to %s", PID_FILE); len=strlen(CHOWNCMD)+1+strlen(config->runasuser)+1+strlen(config->runasuser)+1+strlen(config->pidfile)+1; snprintf(chownit, len, "%s %s:%s %s", CHOWNCMD, config->runasuser, config->runasuser, config->pidfile); if ((chowncmd=popen(chownit, "r"))==NULL){ do_log(LOG_ALERT, "ERR: Can't '%s' !!!", chowncmd); return SCANNER_RET_ERR; } ret=pclose(chowncmd); cuid=getuid(); if (cuid==0){ do_log(LOG_NOTICE, "Changing uid (we are root)"); pws = getpwnam(config->runasuser); if (pws == NULL) do_log(LOG_EMERG,"ERR: Unknown User '%s'",config->runasuser); setuid(pws->pw_uid); } cuid=getuid(); pws = getpwuid(cuid); do_log(LOG_NOTICE, "Running as user: %s",pws->pw_name); if (p3signal(SIGCHLD, do_sigchld)<0) do_log(LOG_EMERG, "ERR: Could not set signal handler SIGCHLD"); if (p3signal(SIGTERM, do_sigterm_main)<0) do_log(LOG_EMERG, "ERR: Could not set signal handler SIGTERM main"); if (p3signal(SIGINT, do_sigterm_main)<0) do_log(LOG_EMERG, "ERR: Could not set signal handler SIGINT main"); if (config->scanner->init1){ if (config->scanner->init1()!=0){ do_log(LOG_CRIT, "ERR: Scanner init failed! Check config and restart " PROGNAME); config->scannerenabled = 0; }else config->scannerenabled = 1; }else config->scannerenabled = 1; if (config->quiet){ config->quiet=0; do_log(LOG_NOTICE, "%s %s started.", PROGNAME, VERSION); config->quiet=1; } if (config->debug){ do_log(LOG_DEBUG,"p3scan.conf:"); do_log(LOG_DEBUG,"pidfile: %s",config->pidfile); do_log(LOG_DEBUG,"maxchilds: %i",config->maxchilds); if (!ntohs(config->addr.sin_addr.s_addr)){ do_log(LOG_DEBUG,"ip: Any"); } else do_log(LOG_DEBUG,"ip: %i",ntohs(config->addr.sin_addr.s_addr)); do_log(LOG_DEBUG,"port: %d",htons(config->addr.sin_port)); if (htonl(INADDR_ANY) == (config->targetaddr.sin_addr.s_addr)) do_log(LOG_DEBUG,"targetip/port disabled"); else { do_log(LOG_DEBUG,"targetip: %i",ntohs(config->targetaddr.sin_addr.s_addr)); do_log(LOG_DEBUG,"targetport: %d",htons(config->targetaddr.sin_port)); } do_log(LOG_DEBUG,"user: %s",config->runasuser); do_log(LOG_DEBUG,"notifydir: %s",config->notifydir); do_log(LOG_DEBUG,"virusdir: %s",config->virusdir); if(config->delit) do_log(LOG_DEBUG,"justdelete: enabled"); else do_log(LOG_DEBUG,"justdelete: disabled"); do_log(LOG_DEBUG,"bytesfree: %lu",config->freespace); #ifdef DEMIME if(config->demime) do_log(LOG_DEBUG,"demime: enabled"); else do_log(LOG_DEBUG,"demime: disabled"); #else do_log(LOG_DEBUG,"DEMIME - Not Available!"); #endif if(strlen(NONULL(config->virusscanner))) do_log(LOG_DEBUG,"scanner: %s",config->virusscanner); if(strlen(NONULL(config->virusregexp))) do_log(LOG_DEBUG,"virusregexp: %s",config->virusregexp); if(config->broken) do_log(LOG_DEBUG,"broken: enabled"); else do_log(LOG_DEBUG,"broken: disabled"); if(config->checkspam) do_log(LOG_DEBUG,"checkspam: enabled"); else do_log(LOG_DEBUG, "checkspam: disabled"); if(strlen(NONULL(config->spamcheck))) do_log(LOG_DEBUG,"spamcheck: %s",config->spamcheck); if(strlen(NONULL(config->renattach))) do_log(LOG_DEBUG,"renattach: %s",config->renattach); if(strlen(NONULL(config->overwrite))) do_log(LOG_DEBUG,"overwrite: %s",config->overwrite); do_log(LOG_DEBUG,"debug: enabled"); if(config->quiet) do_log(LOG_DEBUG,"quiet: enabled"); else do_log(LOG_DEBUG,"quiet: disabled"); if(strlen(NONULL(config->virustemplate))) do_log(LOG_DEBUG,"template: %s",config->virustemplate); if(strlen(NONULL(config->subject))) do_log(LOG_DEBUG,"subject: %s",config->subject); if(strlen(NONULL(config->notify))) do_log(LOG_DEBUG,"notify: %s",config->notify); if(strlen(NONULL(config->extra))) do_log(LOG_DEBUG,"extra: %s",config->extra); do_log(LOG_DEBUG,"emailport: %i",config->smtpport); if(strlen(NONULL(config->smtprset))) do_log(LOG_DEBUG,"smtprset: %s",config->smtprset); if(config->smtpsize) do_log(LOG_DEBUG,"smtpsize: %i",config->smtpsize); else do_log(LOG_DEBUG,"smtpsize: not checking."); do_log(LOG_DEBUG,"sslport: %i",config->sslport); if(strlen(NONULL(config->mail))) do_log(LOG_DEBUG,"mail: %s",config->mail); do_log(LOG_DEBUG,"timeout: %i",config->timeout); if(strlen(NONULL(config->footer))) do_log(LOG_DEBUG,"footer: %s",config->footer); if(config->altemail) do_log(LOG_DEBUG,"altvnmsg: enabled"); else do_log(LOG_DEBUG,"altvnmsg: disabled"); if(config->useurl) do_log(LOG_DEBUG,"useurl: enabled"); else do_log(LOG_DEBUG,"useurl: disabled"); if(strlen(NONULL(config->emergcon))) do_log(LOG_DEBUG,"emergcon: %s",config->emergcon); if (config->enabletop) do_log(LOG_DEBUG,"TOP processing enabled"); else do_log(LOG_DEBUG,"TOP processing disabled"); do_log(LOG_DEBUG,"PIPELINING processing disabled"); do_log(LOG_DEBUG,"STLS processing disabled"); } numprocs=0; do_log(LOG_DEBUG, "Waiting for connections....."); while ((connfd = accept(sockfd, (struct sockaddr *)&addr,&socksize)) >= 0){ if ((abortfd=open(ABORTFILE,O_RDONLY))>=0){ do_log(LOG_DEBUG,"Aloha No Ka ko"); close(abortfd); unlink(ABORTFILE); do_sigterm_main(-1); exit(1); } if ((pid=fork())>0){ /* parent */ numprocs++; do_log(LOG_DEBUG, "Forked, pid=%i, numprocs=%i", pid, numprocs); close (connfd); /* wir brauchen die nicht, der childprocess kmmert sich drum we don't need "them" (connfd?), child process takes care of that */ if (numprocs>=config->maxchilds){ do_log(LOG_WARNING, "MAX_CHILDS (%i) reached!", config->maxchilds); while (1){ pid=waitpid(-1, &stat, 0); /* blocking */ if (do_sigchld_check(pid, stat)) break; } } }else{ /* child */ config->child=1; if ( statvfs( config->virusdir, &fs ) == SCANNER_RET_ERR){ config->emergency="Unable to get available space!"; do_log(LOG_EMERG, "ERR: Unable to get available space!"); return SCANNER_RET_CRIT; // Should never reach here, but keep it clean. :) } if (fs.f_bsize==1024) kbfree=fs.f_bavail; else kbfree=fs.f_bsize * (fs.f_bavail / 1024) + fs.f_bavail%1024 * fs.f_bsize / 1024; if (config->freespace != 0 && kbfree < config->freespace){ config->emergency=make_message("Not enough space! Available space: %lu", kbfree); do_log(LOG_EMERG, "ERR: Not enough space! Available space: %lu", kbfree); do_sigterm_proxy(1); exit(0); } virusdirlen=strlen(config->virusdirbase)+20; config->virusdir=w_malloc(virusdirlen); snprintf(config->virusdir, virusdirlen, "%s/children/%d/", config->virusdirbase,getpid()); do_log(LOG_DEBUG, "setting the virusdir to %s", config->virusdir); if(clean_child_directory(getpid())){ config->emergency="Error calling clean child directory!"; do_log(LOG_EMERG, "ERR: Error calling clean child directory!"); } if((mkdir (config->virusdir, S_IRWXU)<0)){ config->emergency=make_message("Could not create virusdir %s", config->virusdir); do_log(LOG_EMERG,"ERR: Could not create virusdir %s",config->virusdir); } if (p3signal(SIGCHLD, NULL)<0) { config->emergency="Could not set signal handler SIGCHLD NULL"; do_log(LOG_EMERG, "ERR: Could not set signal handler SIGCHLD NULL"); /* unset signal handler for child */ } if (p3signal(SIGPIPE, SIG_IGN)<0) { config->emergency="Could not set signal handler SIGPIPE"; do_log(LOG_EMERG, "ERR: Could not set signal handler SIGPIPE"); /* don't die on SIGPIPE */ } do_log(LOG_DEBUG, "Initialize Context"); p=context_init(); p->client_fd=connfd; p->client_addr=addr; global_p=p; if (p3signal(SIGTERM, do_sigterm_proxy)<0) { config->emergency="Could not set signal handler SIGTERM child"; do_log(LOG_EMERG, "ERR: Could not set signal handler SIGTERM child"); } if (p3signal(SIGINT, do_sigterm_proxy)<0) { config->emergency="Could not set signal handler SIGINT child"; do_log(LOG_EMERG, "ERR: Could not set signal handler SIGINT child"); } do_log(LOG_DEBUG, "starting proxy"); if (proxy(p)){ /* error, but a message has already be sent */ responsemsg=strdup("Critical abort"); }else responsemsg=strdup("Clean Exit"); if (config->scannerenabled){ do_log(LOG_NOTICE, "Session done (%s). Mails: %i Bytes: %lu", responsemsg, p->mailcount, p->bytecount); }else{ do_log(LOG_NOTICE, "Session done (%s). Mails: %i", responsemsg, p->mailcount); } /* responsemsg created w/malloc through strdup */ free(responsemsg); do_sigterm_proxy(-1); exit(0); } } do_log(LOG_NOTICE, "ERR: Accept error - Should not have reached here!"); do_sigterm_main(-1); return 0; } p3scan-2.3.2/p3scan.h0000644000175000001440000002414710347310160013106 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #ifndef _P3SCAN_H #define _P3SCAN_H #include /* do_log */ #include /* config */ #include "getline_ssl.h" #define PROGNAME "P3Scan" #define VERSION "2.3.2" #define MAX_PSEUDO_ARGV 50 #define MESSAGE_NOVIRINFO "" #define FILEDEL "The infected message has been deleted." #define SVRCMD "NOOP" #define PERIOD "." #define BOGUSX "X-P3Scan: Due to an extremely large attachment you see this message line." #define NUL '\0' #define LEN 65536 /* for substr() */ #define CONFIG_STATE_CMDPRE 1 #define CONFIG_STATE_FILE 2 #define CONFIG_STATE_CMD 3 #define SCANNER_INIT_NO 0 #define SCANNER_INIT_OK 1 #define SCANNER_INIT_NULL 2 /* scanner needs no init */ #define SCANNER_INIT_ERR -1 #define SCANNER_RET_OK 0 #define SCANNER_RET_ERR -1 #define SCANNER_RET_VIRUS 2 #define SCANNER_RET_CRIT 3 #define MAX_VIRUS_CODES 16 #if defined(__USE_FILE_OFFSET64) || defined(__USE_LARGEFILE64) #define FSTAT(a, b) fstat64(a,b) #define LSTAT(a, b) lstat64(a,b) #define STAT(a, b) stat64(a,b) #define stat_t stat64 #else #define FSTAT(a, b) fstat(a,b) #define LSTAT(a, b) lstat(a,b) #define STAT(a, b) stat(a,b) #define stat_t stat #endif #ifndef TMP_MAX # define TMP_MAX 238328 #endif #define SETIFNULL(a,b) if (!a) a=b /* wow, that's magic */ #define NONULL(x) ( x==NULL ? "" : x) /* this is nice, found in the mutt code */ #define TRIM(a) \ { \ int len=0; \ while ((a)[len] && isspace((a)[len])) len++; \ memmove((a),&(a)[len], strlen(&(a)[len])+1); \ while ((len=strlen(a))>0 && isspace((a)[len-1])) (a)[len-1]='\0'; \ } /* default configuration, anything can be changed at runtime */ #define LETTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" #define PORT_NUMBER 8110 #define SMTP_PORT 25 #define POP3_PORT 110 #define POP3SPORT 995 #define MAX_CHILDS 10 #define TIMEOUT 30 #define RUNAS_USER "mail" #define VIRUS_DIR "/var/spool/p3scan" #define ABORTFILE "/var/spool/p3scan/p3scan.abort" #define NOTIFY_MAIL_DIR "/var/spool/p3scan/notify" #define VIRUS_SCANNER NULL #define VIRUS_SCANNER_VIRUSCODE 1 #define PID_FILE "/var/run/p3scan/p3scan.pid" #define SYSLOG_NAME "p3scan" #define CONFIGFILE "/etc/p3scan/p3scan.conf" #define VIRUS_TEMPLATE "/etc/p3scan/p3scan.mail" #define DEBUG 0 #define QUIET 0 #define OVERWRITE NULL #define CHECKSPAM 0 #define SPAMCHECK "/usr/bin/spamc" #define MINSPACE 10000 #define DELIT 0 #define SUBJECT "[Virus] found in a mail to you:" #define NOTIFY "Per instruction, the message has been deleted." #define SMTPRSET "Virus detected! P3scan rejected message!" #define XMAIL "/bin/mail" #define FOOTER "/etc/p3scan/p3scan.footer" #define EXTRA "/etc/p3scan/p3scan.extra" #define EMERGCON "root@localhost postmaster@localhost" /* Defaut maximum mail size for scanning. ZERO for no limit! */ #define MAX_SIZE_SCAN 0 /* TOS: do not set, or use IPTOS_[LOWDELAY|THROUGHPUT|RELIABILITY|LOWCOST] */ #define SET_TOS IPTOS_THROUGHPUT #define MOVEIT "/bin/mv" #define COPYIT "/bin/cp" #define POK "+OK" #undef DEBUG_MEM /* print meminfo every log message when debugging */ #undef DEBUG_MESSAGE /* print message lines */ #undef DEBUG_SCANNING /* print message lines while scanning */ #undef DEBUG_FOOTER /* print virus scanner version info */ #undef DEBUG_SMTP /* print smtp messages lines */ /* undef this to not use the ripmime library You also need to change the symlink for Makefile to the proper Makefile. Either Makefile-ripmime or Makefile-noripmime */ #define DEMIME /* print TIME with debug output */ #define DTIME // Logging options defaults #ifndef LOGOPT #define LOGOPT LOG_PID|LOG_CONS #endif #ifndef LOGFAC #define LOGFAC LOG_DAEMON /* To log to file: In the Makefile, change LOG_DAEMON to: LOGFAC="LOG_LOCAL0" or if not defined there, comment above and uncommment below. Add to /etc/syslog.conf (and restart it): local0.* -/var/log/p3scan */ //#define LOGFAC LOCAL0 #endif /* default configuration ends here */ typedef struct proxycontext { struct linebuf *serverbuf; struct linebuf *clientbuf; struct linebuf *hdrbuf; struct sockaddr_in client_addr; struct sockaddr_in server_addr; struct hostent *server_host; size_t socksize; off_t hdroffset; time_t now; int client_fd; int server_fd; int header_fd; int ismail; unsigned int msgnum; int header_exists; int fakehdrdone; int notified; int noop; int errmsg; struct paramlist *params; int mailcount; unsigned long bytecount; int gobogus; int boguspos; int hdrdate; int hdrfrom; int hdrto; char mailfile[4096]; char maildir[4096]; /* mailfile.content */ char p3shdrfile[4096]; char cbuff[1]; char * filename; char * scanthis; /* depending on demime linked to mailfile / maildir */ char * virinfo; /* has to be filled from the scanner */ char * filestatus; /* infected mail kept or deleted */ int scannerinit; /* see SCANNER_INIT_* */ char * extrasubj; /* extra notification subject line */ int cksmtp; /* if scanning an smtp submission */ int checksmtp; /* used to bypass checking for smtp */ int topping; int posttop; int usessl; SSL * ssl; SSL_CTX * ctx; BIO * sbio; char vnmsg[4096]; char * dspamuser; char * actsvr; int actport; int extra; } proxycontext; typedef struct scanner_t { char *name; char *descr; int (*init1)(void); int (*init2)(struct proxycontext *); int (*scan)(struct proxycontext *, char ** virname); void (*uninit2)(struct proxycontext *); void (*uninit1)(void); int dirscan; } scanner_t; typedef struct configuration_t { int maxchilds; char * renattach; char * runasuser; char * virusdirbase; char * virusdir; char * notifydir; char * virustemplate; char * virusscanner; int viruscode[MAX_VIRUS_CODES + 1]; int gvcode[MAX_VIRUS_CODES + 1]; char * virusregexp; int virusregexpsub; int demime; char * pidfile; char * syslogname; char * configfile; int debug; char * overwrite; struct sockaddr_in addr; struct sockaddr_in targetaddr; scanner_t * scanner; int scannerenabled; int quiet; int checkspam; char * spamcheck; unsigned long freespace; int delit; int ispam; int broken; char * subject; char * notify; char * ispspam; char * extra; char * smtprset; int smtpsize; char * clamdserver; char * clamdport; int sslport; char * mail; int timeout; char * footer; int altemail; int smtpport; int enabletop; int child; int useurl; char * emergcon; char * emergency; } configuration_t; extern void do_log(int level, const char *fmt,...); void context_uninit(struct proxycontext * p); // The structure we use to maintain a list of allocated memory typedef struct memory_list { void *address; size_t size; struct memory_list *next; struct memory_list *prev; } memory_list; // The global memory allocation list used by the memory management wrappers memory_list *memory_list_head; // The global couter indicating the number of bytes allocated unsigned long global_memory_count; // A one-time initialization function to setup the memory list and global memory count variables void w_memory_init(void); typedef void Sigfunc(int); #endif p3scan-2.3.2/Makefile-ripmime0000644000175000001440000002002510347310160014635 0ustar jlaiusers# P3Scan 2.3.2 # # (C) 2003-2005 by Jack S. Lai # # It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 # by Folke Ashberg . # # It is based upon his program but provides numerous changes to include # scanning pop3 mail for spam, hardening the program, addaption to todays # email environment, and many other changes. # # The initial release of p3scan includes patches made and submitted to the # original project but were never incorporated. # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # This program is released under the GPL with the additional exemption that # compiling, linking, and/or using OpenSSL is allowed." # (http://www.openssl.org/support/faq.html#LEGAL2) PROGS=p3scan RIPMIME=1.4.0.6 DISTNAME="p3scan-2.3.2" # User options LOGOPT="LOG_PID|LOG_CONS" LOGFAC="LOG_DAEMON" LOGSET=-DLOGOPT=${LOGOPT} -DLOGFAC=${LOGFAC} LANG=en CC=gcc SYSINS=install #CFLAGS=-Wall -O3 -march=i686 ${LOGSET} CFLAGS=-Wall -O2 $(LOGSET) #ifdef DEMIME :) LDFLAGS=-L. -lripmime -lpcre -lssl -lcrypto #-static #else #LDFLAGS=-L. -lpcre -lssl -lcrypto #endif :) PREFIX=/usr DESTDIR= #MANDIR=$(PREFIX)/share/man/man8 MANDIR=$(PREFIX)/man/man8 piddir=/var/run/$(PROGS) datadir=/var/spool/$(PROGS) notify=/var/spool/$(PROGS)/notify userdir=/etc/$(PROGS) docdir=/usr/doc/$(DISTNAME) user=mail:mail # End user options OBJECTS=getlinep3.o getline_ssl.o parsefile.o p3scan.o \ scanner_basic.o scanner_avpd.o scanner_avpd_new.o \ scanner_trophie.o scanner_clamd.o scanner_bash.o LIBS=libripmime.a all: $(PROGS) ripmime/libripmime.a: cd ripmime && $(MAKE) libripmime || exit 1 libripmime.a: ripmime/libripmime.a ln -sf ripmime/libripmime.a libripmime.a p3scan: $(OBJECTS) $(LIBS) $(CC) -o $@ $(OBJECTS) $(LDFLAGS) .c.o: @# generic for all c .o $(CC) $(CFLAGS) -c $< dep depend .dep: @echo "creating depencies" rm -f .tmp.dep @find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0rt $(CC) -M $(CFLAGS) >>.tmp.dep mv .tmp.dep .dep install: p3scan $(SYSINS) -v -m 550 --strip p3scan $(PREFIX)/sbin/ @if [ -f $(DESTDIR)/etc/sysconfig/init ] ; then $(SYSINS) -v -m 755 p3scan-init.d $(DESTDIR)/etc/init.d/p3scan; fi @if [ -f $(DESTDIR)/etc/rc.d/rc.p3scan ] ; then \ echo "rc.p3scan already exists, copying to $(DESTDIR)/etc/rc.d/rc.p3scan.new" ; \ $(SYSINS) -v -m 660 rc.p3scan $(DESTDIR)/etc/rc.d/rc.p3scan.new ; \ else \ $(SYSINS) -v -m 755 rc.p3scan $(DESTDIR)/etc/rc.d ; \ fi @if test -d $(DESTDIR)$(piddir); then echo "$(DESTDIR)$(piddir) exists, not creating."; else mkdir -p $(DESTDIR)$(piddir); fi @if test -d $(DESTDIR)$(userdir); then echo "$(DESTDIR)$(userdir) exits, not creating."; \ else mkdir -p $(DESTDIR)$(userdir); chown $(user) $(DESTDIR)$(userdir); fi @if [ -f $(DESTDIR)$(userdir)/p3scan.conf ] ; then \ echo "$(DESTDIR)$(userdir)/p3scan.conf already exists, copying to $(DESTDIR)$(userdir)/p3scan.conf.sample" ; \ $(SYSINS) -v -m 600 p3scan.conf $(DESTDIR)$(userdir)/p3scan.conf.sample ; \ else \ $(SYSINS) -v -m 600 p3scan.conf $(DESTDIR)$(userdir)/ ; \ fi @if [ -f $(DESTDIR)$(userdir)/p3scan.mail ] ; then \ echo "$(DESTDIR)$(userdir)/p3scan.mail already exists, renaming to $(DESTDIR)$(userdir)/p3scan.mail.old" ; \ mv $(DESTDIR)$(userdir)/p3scan.mail $(DESTDIR)$(userdir)/p3scan.mail.old ; \ fi ln -sf $(DESTDIR)$(userdir)/p3scan-$(LANG).mail $(DESTDIR)$(userdir)/p3scan.mail @if test -d $(DESTDIR)$(datadir)/children; then echo "$(DESTDIR)$(datadir)/children exists, not creating."; else mkdir -p $(DESTDIR)$(datadir)/children; fi @if test -d $(DESTDIR)$(datadir)/notify; then echo "$(DESTDIR)$(datadir)/notify exists, not creating."; else mkdir $(DESTDIR)$(datadir)/notify; fi @chown $(user) $(DESTDIR)$(piddir) @chown -R $(user) $(DESTDIR)$(datadir) @chmod -R 700 $(DESTDIR)$(datadir) $(SYSINS) -v -m 644 p3scan.8.gz $(DESTDIR)$(MANDIR) $(SYSINS) -v -m 644 p3scan_readme.8.gz $(DESTDIR)$(MANDIR) @if test -d $(DESTDIR)$(docdir); then echo "$(DESTDIR)$(docdir) exists, not creating."; else mkdir -p $(DESTDIR)$(docdir); fi $(SYSINS) -v -m 644 AUTHORS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 CHANGELOG $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 CONTRIBUTERS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 LICENSE $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 LICENSE.OpenSSL $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 NEWS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README-ripmime $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README-rpm $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 spamfaq.html $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 TODO.list $(DESTDIR)$(docdir) # Add translated mail files here: $(SYSINS) -v -m 644 p3scan-??-??.mail $(DESTDIR)$(userdir) $(SYSINS) -v -m 644 p3scan-??.mail $(DESTDIR)$(userdir) clean: cd ripmime && $(MAKE) clean rm -f ripmime/libripmime.a rm -f ripmime/ripOLE/ripole rm -f $(DISTNAME).tar.* $(PROGS) $(OBJECTS) $(LIBS) .dep core uninstall: @if [ -f $(PREFIX)/sbin/$(PROGS) ] ; then rm -f $(PREFIX)/sbin/$(PROGS) ; fi @if [ -f /etc/init.d/p3scan ] ; then rm -f /etc/init.d/p3scan ; fi @if [ -f /etc/rc.d/rc.p3scan ] ; then rm -f /etc/rc.d/rc.p3scan ; fi @if [ -f $(MANDIR)/p3scan.8.gz ] ; then rm -r $(MANDIR)/p3scan.8.gz ; fi @if [ -f $(MANDIR)/p3scan_readme.8.gz ] ; then rm -r $(MANDIR)/p3scan_readme.8.gz ; fi @if [ -f $(userdir)/p3scan.conf.sample ] ; then rm -f $(userdir)/p3scan.conf.sample ; fi @if [ -f $(userdir)/p3scan.mail.sample ] ; then rm -f $(userdir)/p3scan.mail.sample ; fi @if [ -d $(docdir) ] ; then rm -rf $(docdir) ; fi @if [ -f $(MANDIR)/p3scan.8.gz ] ; then rm -f $(MANDIR)/p3scan.8.gz ; fi @if [ -f $(MANDIR)/p3scan_readme.8.gz ] ; then rm -f $(MANDIR)/p3scan_readme.8.gz ; fi @{ test ! -d $(datadir) || { find $(datadir) -type d ! -perm -700 -exec chmod u+w {} ';' && rm -fr $(datadir); }; } @{ test ! -d $(piddir) || { find $(piddir) -type d ! -perm -700 -exec chmod u+w {} ';' && rm -fr $(piddir); }; } @echo "$(PROGS), $(datadir), and samples removed." @if [ -f $(userdir)/p3scan.conf ] ; then echo "$(userdir)/p3scan.conf remains." ; fi @if [ -f $(userdir)/p3scan.mail ] ; then echo "$(userdir)/p3scan.mail and p3scan-??.mail remains." ; fi tags: @#VIM Users know why! *g* ctags --c-types=+c+p+f+x -R . fulltags: @#VIM Users know why! *g* find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0r $(CC) -M -H $(CFLAGS) 2>.totag >/dev/null find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0r echo >>.totag cat .totag | sed "s/^[[:space:]]*//" | grep -v "^$(CC)" | sort | uniq >.totag ctags --c-types=+c+p+f+x -L .totag . rm -f .totag dist: rm -rf $(DISTNAME) mkdir $(DISTNAME) $(MAKE) clean cp -prd Makefile* getline*.{c,h} parsefile.{c,h} p3scan.{c,h} $(DISTNAME) cp scanner.h scanner_*.c $(DISTNAME) cp p3scan.conf p3scan-??.mail p3scan-??-??.mail rc.p3scan $(DISTNAME) cp p3scan.8.gz p3scan_readme.8.gz $(DISTNAME) cp rc.p3scan p3scan-init.d p3scan.sh $(DISTNAME) cp *.patch $(DISTNAME) cp AUTHORS CHANGELOG CONTRIBUTERS LICENSE LICENSE.OpenSSL NEWS README README-ripmime README-rpm spamfaq.html TODO.list $(DISTNAME) cp -prdv ripmime-$(RIPMIME) $(DISTNAME) cp -rv ripmime $(DISTNAME) rm -f $(DISTNAME).tar.gz* tar cfv $(DISTNAME).tar $(DISTNAME)/ rm -rf $(DISTNAME) gzip -9f $(DISTNAME).tar md5sum $(DISTNAME).tar.gz > $(DISTNAME).tar.gz.md5 @echo "Now create signature with:" @echo "gpg -a -o $(DISTNAME).tar.gz.asc -b $(DISTNAME).tar.gz" # gpg -a -o $(DISTNAME).tar.gz.asc -b $(DISTNAME).tar.gz .PHONY: clean install all tags fulltags dist dep depend #include .dep p3scan-2.3.2/AUTHORS0000644000175000001440000000054310347310635012617 0ustar jlaiusersP3Scan is written and maintained by: Jack S. Lai RPM packages are maintained by: Gabriele Carioli Original code taken from pop3vscan 0.4 by: Folke Ashburg Please see the CONTRIBUTERS file for a listing of people that have contributed code to this project. p3scan-2.3.2/scanner_bash.c0000644000175000001440000002431210347310635014341 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include "p3scan.h" #include "parsefile.h" extern int checkbuff(int fdc); extern int checktimeout(struct proxycontext *p); extern char *strreplace(char *haystack,char *needle,char *rstr); struct configuration_t * config; extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); static int scan(struct proxycontext *p, char ** virname){ int ret,ret2,fdc; char * command; int len; FILE * scanner; static char line[4096*16]; pcre * rx; const char *pcre_error; int pcre_erroffset; int offsets[50]; #define VISIZE 1000 char *vi=w_malloc(VISIZE); int vipos = 0; int vcodndx; do_log(LOG_DEBUG, "Bash scanner says hello"); if (vi==NULL) return SCANNER_RET_ERR; if (config->virusregexp){ rx = pcre_compile(config->virusregexp, PCRE_UNGREEDY /* | PCRE_CASELESS */ , &pcre_error, &pcre_erroffset, NULL); if (!rx) { /* should not happen, because init1 has already tested and set to NULL on error */ do_log(LOG_WARNING, "Ouch! Can't compile regular expression: %s (char %i)", pcre_error, pcre_erroffset); } } else { rx=NULL; } /* %MAILFROM% %MAILTO% %USERNAME% %SUBJECT% %MAILDATE% %SERVERIP% %SERVERPORT% %CLIENTIP% %CLIENTPORT% %PROTOCOL% %PROGNAME% %VERSION% %VDINFO% */ len=strlen(config->virusscanner) + 1 + strlen(p->scanthis) + 1; if (strlen(NONULL(paramlist_get(p->params, "%MAILFROM%")))) len=len+strlen(paramlist_get(p->params, "%MAILFROM%"))+3; else{ len=len+9; paramlist_set(p->params, "%MAILFROM%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%MAILTO%")))) len=len+strlen(paramlist_get(p->params, "%MAILTO%"))+3; else{ len=len+9; paramlist_set(p->params, "%MAILTO%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%USERNAME%")))) len=len+strlen(paramlist_get(p->params, "%USERNAME%"))+3; else{ len=len+9; paramlist_set(p->params, "%USERNAME%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%SUBJECT%")))) len=len+strlen(paramlist_get(p->params, "%SUBJECT%"))+3; else{ len=len+9; paramlist_set(p->params, "%SUBJECT%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%MAILDATE%")))) len=len+strlen(paramlist_get(p->params, "%MAILDATE%"))+3; else{ len=len+9; paramlist_set(p->params, "%MAILDATE%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%SERVERIP%")))) len=len+strlen(paramlist_get(p->params, "%SERVERIP%"))+3; else{ len=len+9; paramlist_set(p->params, "%SERVERIP%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%SERVERPORT%")))) len=len+strlen(paramlist_get(p->params, "%SERVERPORT%"))+3; else{ len=len+9; paramlist_set(p->params, "%SERVERPORT%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%CLIENTIP%")))) len=len+strlen(paramlist_get(p->params, "%CLIENTIP%"))+3; else{ len=len+9; paramlist_set(p->params, "%CLIENTIP%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%CLIENTPORT%")))) len=len+strlen(paramlist_get(p->params, "%CLIENTPORT%"))+3; else{ len=len+9; paramlist_set(p->params, "%CLIENTPORT%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%PROTOCOL%")))) len=len+strlen(paramlist_get(p->params, "%PROTOCOL%"))+3; else{ len=len+9; paramlist_set(p->params, "%PROTOCOL%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%PROGNAME%")))) len=len+strlen(paramlist_get(p->params, "%PROGNAME%"))+3; else{ len=len+9; paramlist_set(p->params, "%PROGNAME%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%VERSION%")))) len=len+strlen(paramlist_get(p->params, "%VERSION%"))+3; else{ len=len+9; paramlist_set(p->params, "%VERSION%", "(null)"); } if (strlen(NONULL(paramlist_get(p->params, "%VDINFO%")))) len=len+strlen(paramlist_get(p->params, "%VDINFO%"))+3; else{ len=len+9; paramlist_set(p->params, "%VDINFO%", "(null)"); } len=len+30; command=w_malloc(len+1); snprintf(command, len, "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' 2>&1 ", config->virusscanner, p->scanthis, strreplace(paramlist_get(p->params, "%MAILFROM%" ),"'"," "), strreplace(paramlist_get(p->params, "%MAILTO%" ),"'"," "), strreplace(paramlist_get(p->params, "%USERNAME%" ),"'"," "), strreplace(paramlist_get(p->params, "%SUBJECT%" ),"'"," "), strreplace(paramlist_get(p->params, "%MAILDATE%" ),"'"," "), paramlist_get(p->params, "%SERVERIP%" ), paramlist_get(p->params, "%SERVERPORT%"), paramlist_get(p->params, "%CLIENTIP%" ), paramlist_get(p->params, "%CLIENTPORT%"), paramlist_get(p->params, "%PROTOCOL%" ), paramlist_get(p->params, "%PROGNAME%" ), paramlist_get(p->params, "%VERSION%" ), strreplace(paramlist_get(p->params, "%VDINFO%" ),"'"," ")); do_log(LOG_DEBUG, "popen %s", command); if ((scanner=popen(command, "r"))==NULL){ do_log(LOG_ALERT, "Can't start scanner '%s' !!!", command); w_free(vi); w_free(command); return SCANNER_RET_ERR; } fdc=fileno(scanner); ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; while (!ret2){ ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } vi[0]='\0'; *virname=vi; while ((fgets(line, 4095, scanner))!=NULL){ line[strlen(line)-1]='\0'; #ifdef DEBUG_SCANNING do_log(LOG_DEBUG, "ScannerLine: '%s'", line); #endif if (rx){ ret = pcre_exec(rx, NULL, line, strlen(line), 0, 0, offsets, 50); if (ret > config->virusregexpsub){ len=pcre_copy_substring(line, offsets, ret, config->virusregexpsub, vi+vipos, VISIZE - vipos -4 ); if (len==PCRE_ERROR_NOMEMORY) break; if (len>0) vipos+=len; vi[vipos]=' '; vipos++; vi[vipos]='&'; vipos++; vi[vipos]=' '; vipos++; } } } ret=pclose(scanner); //--free(command); w_free(command); if (vipos > 3) vi[vipos-3]='\0'; do_log(LOG_DEBUG, "vi : '%s'", vi); if (rx) pcre_free(rx); if (!WIFEXITED(ret)){ do_log(LOG_ALERT, "Scanner returned abnormal signal (%i)", ret); return SCANNER_RET_ERR; } else do_log(LOG_DEBUG, "Scanner returned signal %i", WEXITSTATUS(ret)); ret=WEXITSTATUS(ret); for (vcodndx = 0; vcodndx < MAX_VIRUS_CODES && config->viruscode[vcodndx] != -1; vcodndx++) { if (ret == config->viruscode[vcodndx]) return SCANNER_RET_VIRUS; /* contains a virus */ } for (vcodndx = 0; vcodndx < MAX_VIRUS_CODES && config->gvcode[vcodndx] != -1; vcodndx++) { if (ret == config->gvcode[vcodndx]){ ret = 0; do_log(LOG_DEBUG, "Basic scanner says goodbye (goodcode)"); return SCANNER_RET_OK; /* good return code */ } } if (ret!=0){ do_log(LOG_ALERT, "WARNING: Your scanner returned neither 0, a viruscode, nor a good viruscode, but %i", ret); return SCANNER_RET_ERR; } do_log(LOG_DEBUG, "Basic scanner says goodbye"); return SCANNER_RET_OK; /* all ok, no virus */ } static int init1(void){ pcre * rx; const char *pcre_error; int pcre_erroffset; if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. scanning completely disabled"); return SCANNER_RET_ERR; } if (strlen(NONULL(config->virusregexp))>0){ rx = pcre_compile(config->virusregexp, PCRE_UNGREEDY /* | PCRE_CASELESS */ , &pcre_error, &pcre_erroffset, NULL); if (!rx) { do_log(LOG_WARNING, "Can't compile regular expression: %s (char %i). Virusnames can't be extracted", pcre_error, pcre_erroffset); config->virusregexp=NULL; } else { do_log(LOG_DEBUG, "RX compiled succesfully"); pcre_free(rx); } } else { config->virusregexp=NULL; do_log(LOG_WARNING, "No Regular Expression given! Virusnames can't be extracted"); } return 0; } scanner_t scanner_bash = { "bash", /* name */ "Bash file invocation scanner", /* description */ &init1, /* init1 (once, afer startup) */ NULL, /* init2 (every connection before first mail) */ &scan, /* scan */ NULL, /* uninit2 */ NULL, /* uninit1 */ 1 /* dirscan */ }; p3scan-2.3.2/getline_ssl.c0000644000175000001440000002021610347310160014213 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * This module modified by Simon Santesteban. * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getline_ssl.h" extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); static char *pass; static int password_cb(char *buf,int num, int rwflag,void *userdata); /*The password code is not thread safe*/ static int password_cb(char *buf,int num, int rwflag,void *userdata) { if(num0) return 1; rc=SSL_pending(ssl); if(rc>0) { //printf("Hay algo para leer!!\n"); return 1; } else if(rc==-1) { perror("SSL_pending"); return -1; } return rc; } int getline_ssl(SSL * ssl, struct linebuf * l){ int len=0, last_r, maxread; char *p; /* is there one more line in buf? */ if (l->lineend == l->bufend){ /* no --> read some chars */ maxread=l->max -1; if (maxread<1) return GETLINE_TOO_LONG; if (select_fd_read_ssl(ssl)<1) return GETLINE_NEED_READ; len=SSL_read(ssl, l->buf, maxread ); if (len<1) return GETLINE_EOF; l->len=len; l->line=l->buf; l->bufend=&(l->buf[len-1]); } else { /* move next line (from which we have already some data) to beginning of buf */ if (l->lineend && l->bufend){ len=l->bufend - l->lineend; memmove(l->buf, l->lineend +1, len ); l->line=l->buf; l->bufend=l->buf + len -1 ; l->len=len; } else { /* there is a not yet finished line in buf */ maxread=l->max - l->len -1; if (maxread<1){ /* no more space left in buf */ if (l->len == l->max){ l->linelen=l->max-1; } else { l->linelen=l->len-1; } l->line=l->buf; /* set to zero, then next call will work */ l->len=0; l->lineend=NULL; l->bufend=NULL; return GETLINE_TOO_LONG; } if (select_fd_read_ssl(ssl)<1) return GETLINE_NEED_READ; len=SSL_read(ssl, &(l->buf[l->len]), maxread ); if (len<1){ /* EOF */ /* TODO: what's up if last line has no [\r]\n ? */ l->moredata=0; l->linelen=GETLINE_LINE_NULL; l->line=l->buf; l->buf[0]='\0'; return GETLINE_EOF; } l->len+=len; l->line=l->buf; l->bufend=&(l->buf[l->len-1]); } } last_r=0; p=l->buf; while (p <= l->bufend ){ switch (*p) { case '\r': last_r=1; break; case '\n': l->lineend=p; *p='\0'; if (last_r) *(p-1)='\0'; l->linelen=strlen(l->line); l->moredata=(l->lineend != l->bufend); return GETLINE_OK; /* voila, there is a line */ break; default: last_r=0; } p++; } l->lineend=NULL; l->linelen=GETLINE_LINE_INCOMPLETE; l->moredata=0; return GETLINE_NOLINE; /* could not complete a whole line */ } int writeline_ssl(SSL * ssl, int leading, char * c){ char * out; int len, res; if (!c) return GETLINE_ERR; //--out=malloc(strlen(c)+3); out=w_malloc(strlen(c)+3); switch (leading){ case WRITELINE_LEADING_NONE: len=sprintf(out, "%s", c); break; case WRITELINE_LEADING_N: len=sprintf(out, "%s\n", c); break; case WRITELINE_LEADING_RN: default: len=sprintf(out, "%s\r\n", c); break; } res=secure_write_ssl(ssl, out, len); //--free(out); w_free(out); return res; /* bytes / GETLINE_ERR / GETLINE_PIPE */ } int writeline_format_ssl(SSL * ssl, int leading , char * fmt, ...){ char out[4096]; int len, res; va_list az; if (!fmt) return GETLINE_ERR; va_start(az,fmt); len=vsnprintf(out, 4090, fmt, az); switch (leading){ case WRITELINE_LEADING_NONE: break; case WRITELINE_LEADING_N: out[len]='\n'; len++; break; case WRITELINE_LEADING_RN: default: out[len]='\r'; len++; out[len]='\n'; len++; break; } res=secure_write_ssl(ssl, out, len); return res; /* bytes / GETLINE_ERR */ } int SSL_create_conn(int fd, struct sockaddr * addr, int addr_len, SSL ** p_ssl, SSL_CTX ** p_ctx, BIO ** p_sbio) { int rc; struct sockaddr_in * direccion; // Build our SSL context if((*p_ctx=initialize_ctx())==NULL) { perror("Error initializing SSL context"); return -1; } // Connect the TCP socket direccion=(struct sockaddr_in *)addr; rc=connect(fd, addr, addr_len); if(rc) { perror("Error connecting"); return rc; } *p_ssl=SSL_new(*p_ctx); *p_sbio=BIO_new_socket(fd,BIO_NOCLOSE); SSL_set_bio(*p_ssl,*p_sbio,*p_sbio); if(SSL_connect(*p_ssl)<=0) { perror("Error doing SSL connection"); close(fd); return -1; // Error } return 0; // OK } void SSL_destroy_conn(int sock, SSL * ssl, SSL_CTX * ctx, BIO * sbio) { SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); close(sock); } p3scan-2.3.2/getline_ssl.h0000644000175000001440000000647610347310160014234 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * This module modified by Simon Santesteban. * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #ifndef _GETLINE_SSL_H #define _GETLINE_SSL_H #include "getlinep3.h" #include #include #define BUFSIZZ 1024 extern BIO *bio_err; int berr_exit (char *string); int err_exit(char *string); SSL_CTX *initialize_ctx(); void destroy_ctx(SSL_CTX *ctx); #ifndef ALLOW_OLD_VERSIONS #if (OPENSSL_VERSION_NUMBER < 0x00905100L) #error "Must use OpenSSL 0.9.6 or later" #endif #endif /* reads a line from fd or l->buf is there any * this function is non blocking. * return values: * GETLINE_OK : there is a (complete line) * GETLINE_NOLINE : we've received data, but no complete line * GETLINE_NEED_READ : we need to read, but select returned no data * GETLINE_EOF : fd is EOF * GETLINE_TOO_LONG : The line can't be retrieved because buf is too * small. The (incomplete) line is accessable! * You can call getline again, but it will return * the rest of that line and not the next one! */ int getline_ssl(SSL * ssl, struct linebuf * l); /* writes line to fd * set WRITELINE_LEADING_[NONE|N|RN] in leading */ int writeline_ssl(SSL * ssl, int leading, char * c); /* writes a formatted line to fd * set WRITELINE_LEADING_[NONE|N|RN] in leading */ int writeline_format_ssl(SSL * ssl, int leading, char * fmt, ...); /* write len bytes of buf to fd * if fd can't receive whole len at once, write repeats * until all is sent */ int secure_write_ssl(SSL * ssl, char * buf, int len); /* runs select (read) on fd with a timeval of 0 (not NULL!) */ int select_fd_read_ssl(SSL * ssl); /* creates and destroy ssl connection */ int SSL_create_conn(int fd, struct sockaddr * addr, int addr_len, SSL ** p_ssl, SSL_CTX ** p_ctx, BIO ** p_sbio); void SSL_destroy_conn(int sock, SSL * ssl, SSL_CTX * ctx, BIO * sbio); #endif /* ifndef _GETLINE_H */ p3scan-2.3.2/scanner_avpd.c0000644000175000001440000002540110347310635014356 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "p3scan.h" #define DEFAULT_SOCKET_PATH "/var/run" struct configuration_t * config; extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); typedef unsigned long ULONG; #define FL_GETVERSION 0x04 // ... Cfg pathnames //#define AVP_NODE_DEFDIR "/var/run" #define AVP_NODE_PID "AvpPid" #define AVP_NODE_CTL "AvpCtl\0\0\0" static char *NodePid; static char *NodeCtl; static int avp_fd; // fd for log static int connected; // have done connect static struct sockaddr_un avp_socket; // AF_UNIX address of local logger static int avp_socket_connect(struct proxycontext *p){ if (avp_fd == -1){ bzero((char *)&avp_socket, sizeof(avp_socket)); avp_socket.sun_family=AF_UNIX; strcpy(avp_socket.sun_path, NodeCtl); if ((avp_fd=socket(AF_UNIX,SOCK_STREAM,0)) < 0 ){ do_log(LOG_CRIT, "create socket error: socket() not created %s", NodeCtl); return -1; } } if (avp_fd!=-1 && connected==-1){ do_log(LOG_DEBUG, "Trying to connect to socket"); if (connect(avp_fd, (struct sockaddr *)(&avp_socket), sizeof(avp_socket.sun_family) + strlen(NodeCtl)) >= 0){ connected=1; do_log(LOG_DEBUG, "avp_socket_connect connected to kavdaemon"); return 0; } } else { do_log(LOG_DEBUG, "Already connected"); return 0; } do_log(LOG_CRIT, "can't connect to socket %s", NodeCtl); return -1; } static void avp_socket_close(void){ close(avp_fd); avp_fd=-1; connected=0; do_log(LOG_DEBUG, "avp_socket_close"); } /* avp_sendcommand * return codes: * >=0: OK, avpd returncode * -1: write error * -2: read error * -3: error */ static int avp_sendcommand(struct proxycontext * p, int flags, char *buftoscan, ULONG *ulFlags, ULONG* buflen, char ** virinfo){ register int len=strlen(buftoscan); char *ResultBuf=NULL; // output the message to the local logger do_log(LOG_DEBUG, "write string (%s) to kavdaemon", buftoscan); *virinfo=NULL; if (write(avp_fd, buftoscan, len+1)>=0){ int Rez; long uintbuf=0; int ExitCode; do_log(LOG_DEBUG, "Wait results:"); if ((Rez=read(avp_fd,(char*)&uintbuf,2))==-1) return -2; ExitCode=(uintbuf&0xff)-0x30; if ((uintbuf&0x000f)!=0xf) //0x3f '?' do_log(LOG_DEBUG, "Test result: %x", ExitCode); else do_log(LOG_DEBUG,"Disinfect queries:"); do_log(LOG_DEBUG, "Test result: 0x%x, flags: 0x%x", uintbuf & 0x00ff, uintbuf & 0xff00 ); ResultBuf=NULL; if ((uintbuf&0xff00)!=0){ /* further actions */ if ((uintbuf&0x200)!=0){ /* where disinfected file is, uninteresting for us */ if ((Rez=read(avp_fd, (char*)buflen, sizeof(ULONG)))==-1) return -2; *ulFlags|=1; } if ((uintbuf&0x100)!=0){ /* we got result string to read */ if ((Rez=read(avp_fd,(char*)&uintbuf,sizeof(ULONG)))==-1) return -2; do_log(LOG_DEBUG, "Result string lenght: %d", uintbuf); //--ResultBuf=(char*)malloc(uintbuf+1); ResultBuf=(char*)w_malloc(uintbuf+1); if(ResultBuf!=NULL){ char *ResultStr=ResultBuf; ResultBuf[0]=0; //if((Rez=recv(avp_fd,ResultStr,uintbuf,0))==-1) return -2; while((uintbuf>0)&&((Rez=recv(avp_fd,ResultStr,uintbuf,0))!=0)){ if(Rez==-2){ //--free(ResultBuf); w_free(ResultBuf); return -2; } else { uintbuf-=Rez; ResultStr[Rez]=0; ResultStr+=Rez; } } } } } switch (ExitCode&0x0f){ case 8: do_log(LOG_WARNING, "Corrupted objects were found"); break; case 7: do_log(LOG_WARNING, "File AvpScanner is corrupted"); break; case 6: do_log(LOG_WARNING, "All viruses deleted"); break; case 5: do_log(LOG_WARNING, "All viruses disinfected"); break; case 4: do_log(LOG_WARNING, "Known viruses were detected"); break; case 3: do_log(LOG_WARNING, "Suspicious objects were found"); break; case 2: do_log(LOG_WARNING, "Warning"); break; case 1: do_log(LOG_WARNING, "Virus scan was not complete"); break; case 0: do_log(LOG_DEBUG, "No viruses were found"); break; case 0xf: { do_log(LOG_CRIT, "AVPD want's to disinfect! Please tell him not to do."); //--free(ResultBuf); w_free(ResultBuf); return -3; } default: do_log(LOG_WARNING, "Error!(test result %d)", Rez); break; } /* switch ExitCode */ switch (ExitCode&0xf0){ case 8: do_log(LOG_CRIT, "Internal error: Integrity failed."); break; case 4: do_log(LOG_CRIT, "Internal error: Bases not found."); break; } do_log(LOG_DEBUG, "Found viruses: '%s'", ResultBuf); //if (ResultBuf!=NULL) free(ResultBuf); *virinfo=ResultBuf; return ExitCode; } /* if write */ return -1; } static int avp_scanfile(struct proxycontext * p, int flags, char * filetoscan, char ** virname){ int rez=-1; char *tbuf; time_t now; int len; ULONG ulFlags=0,ulDiffer=0; char *v, *v2, *virinfo; if(avp_fd<0 || !connected) if (avp_socket_connect(p)!=0) return -1; // build the message len=strlen(filetoscan)+30; //--tbuf=malloc(len+1); tbuf=w_malloc(len+1); (void)time(&now); (void)snprintf(tbuf, len, "<%d>%.15s:%s", flags, ctime(&now)+4, filetoscan); rez=avp_sendcommand(p, flags, tbuf, &ulFlags, &ulDiffer, &virinfo); //do_log(LOG_DEBUG, "Virinfo: '%s'", virinfo); switch (rez){ case -1: do_log(LOG_CRIT, "Error: cannot write to kavdaemon!"); break; case -2: do_log(LOG_CRIT, "Error: cannot read from kavdaemon!"); break; case -3: do_log(LOG_CRIT, "Error occured during avpd conversation"); break; } //--free(tbuf); w_free(tbuf); if (virinfo){ /* process virinfo */ /* format is: infected: EICAR-Test-File */ v=virinfo; /* strip trailing filename */ if (!strncmp(v, filetoscan, strlen(filetoscan))) v+=strlen(filetoscan); /* strip trailing blanks */ while (v[0] && isspace(v[0])) v++; /* strip trailing '[a-z]*:' (if any) */ v2=v; while (v2[0] && isalnum(v2[0])) v2++; if (v2[0]==':') v=v2+1; /* strip trailing blanks */ while (v[0] && isspace(v[0])) v++; /* strip leading blanks */ while ((len=strlen(v))>0 && isspace(v[len-1])) v[len-1]='\0'; do_log(LOG_DEBUG, "virinfo: '%s'", v); *virname=strdup(v); //--free(virinfo); w_free(virinfo); } else *virname=NULL; return rez; } static int init1(void){ int len ; do_log(LOG_DEBUG, "AVP Init1"); if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. we're using " DEFAULT_SOCKET_PATH); config->virusscanner=strdup(DEFAULT_SOCKET_PATH); } len=strlen(config->virusscanner); /* Build the Nodes */ //--if ((NodeCtl=malloc(len + strlen(AVP_NODE_CTL) + 10))==NULL) return -1; //--if ((NodePid=malloc(len + strlen(AVP_NODE_PID) + 10))==NULL) return -1; if ((NodeCtl=w_malloc(len + strlen(AVP_NODE_CTL) + 10))==NULL) return -1; if ((NodePid=w_malloc(len + strlen(AVP_NODE_PID) + 10))==NULL) return -1; strncpy(NodeCtl, config->virusscanner, len + 1); if (config->virusscanner[len-1]!='/') strcat(NodeCtl, "/"); strncpy(NodePid, NodeCtl, strlen(NodeCtl) + 1); strcat(NodeCtl, AVP_NODE_CTL); strcat(NodePid, AVP_NODE_PID); len=strlen(NodeCtl); NodeCtl[len+1]='\0'; NodeCtl[len+2]='\0'; do_log(LOG_DEBUG, "NoteCtl: %s NodePid: %s", NodeCtl, NodePid); connected=-1; avp_fd=-1; do_log(LOG_DEBUG, "AVP Init1 Done"); return 0; } static int init2(struct proxycontext *p){ do_log(LOG_DEBUG, "AVP Init2"); /* Connect to socket */ if (avp_socket_connect(p)!=0) return -1; do_log(LOG_DEBUG, "AVP Init2 Done"); return 0; } static void uninit2(struct proxycontext *p){ avp_socket_close(); } static int scan(struct proxycontext *p, char **virname){ int ret; do_log(LOG_DEBUG, "AVP scanner says hello"); ret=avp_scanfile(p, 0, p->scanthis, virname); if (ret==3 || ret==4) ret = SCANNER_RET_VIRUS; /* virus */ else if (ret<0) ret=SCANNER_RET_ERR; else ret = SCANNER_RET_OK; do_log(LOG_DEBUG, "AVP scanner says goodbye"); return ret; } scanner_t scanner_avpd = { "avpd", /* name */ "Kaspersky AVPDaemon", /* description */ &init1, /* init1 (once, afer startup) */ &init2, /* init2 (every connection before first mail) */ &scan, /* scan */ &uninit2, /* uninit2 */ NULL, /* uninit1 */ 0 /* dirscan */ }; p3scan-2.3.2/p3scan-2.3-pcre.patch0000644000175000001440000000301610347310635015204 0ustar jlaiusersdiff -cr p3scan-2.3.1/scanner_bash.c p3scan-2.3.1-1/scanner_bash.c *** p3scan-2.3.1/scanner_bash.c 2005-11-04 00:04:37.000000000 -0500 --- p3scan-2.3.1-1/scanner_bash.c 2005-11-04 01:08:43.000000000 -0500 *************** *** 36,42 **** #include #include #include ! #include #include "p3scan.h" #include "parsefile.h" --- 36,42 ---- #include #include #include ! #include #include "p3scan.h" #include "parsefile.h" diff -cr p3scan-2.3.1/scanner_basic.c p3scan-2.3.1-1/scanner_basic.c *** p3scan-2.3.1/scanner_basic.c 2005-11-04 00:04:37.000000000 -0500 --- p3scan-2.3.1-1/scanner_basic.c 2005-11-04 01:09:02.000000000 -0500 *************** *** 36,42 **** #include #include #include ! #include #include "p3scan.h" --- 36,42 ---- #include #include #include ! #include #include "p3scan.h" diff -cr p3scan-2.3.1/scanner_clamd.c p3scan-2.3.1-1/scanner_clamd.c *** p3scan-2.3.1/scanner_clamd.c 2005-11-04 00:04:37.000000000 -0500 --- p3scan-2.3.1-1/scanner_clamd.c 2005-11-04 01:09:25.000000000 -0500 *************** *** 34,40 **** #include #include #include ! #include #include #include --- 34,40 ---- #include #include #include ! #include #include #include p3scan-2.3.2/LICENSE.OpenSSL0000644000175000001440000001640210347310635014037 0ustar jlaiusers/* * (C) 2005 by Jack S. Lai * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed. * (http://www.openssl.org/support/faq.html#LEGAL2) * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ LICENSE ISSUES ============== The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ p3scan-2.3.2/TODO.list0000644000175000001440000000110710347310635013206 0ustar jlaiusersEnable use of GNU/Autotools p3scan.c:TODO: Integrity check (check directories/permissions at start) p3scan.c:TODO: Wanted: Header parser p3scan.c:TODO: Wanted: white-list support p3scan.c:TODO: Wanted: no iptables support p3scan.c: /* TODO: Close open descriptors on ANY errors in all this code! - I think I got em all */ p3scan.c: /* TODO: ripmime error checking */ p3scan.c: /* TODO: Fail on unexpected scanner return code. */ p3scan.c:/* TODO: Enable ONLY -d -f -h and -v as command line options. */ parsefile.c: /* TODO: deleteting (value==NULL) does nothing */ p3scan-2.3.2/Makefile-noripmime0000644000175000001440000002003310347310160015171 0ustar jlaiusers# P3Scan 2.3.2 # # (C) 2003-2005 by Jack S. Lai # # It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 # by Folke Ashberg . # # It is based upon his program but provides numerous changes to include # scanning pop3 mail for spam, hardening the program, addaption to todays # email environment, and many other changes. # # The initial release of p3scan includes patches made and submitted to the # original project but were never incorporated. # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # This program is released under the GPL with the additional exemption that # compiling, linking, and/or using OpenSSL is allowed." # (http://www.openssl.org/support/faq.html#LEGAL2) PROGS=p3scan RIPMIME=1.4.0.6 DISTNAME="p3scan-2.3.2" # User options LOGOPT="LOG_PID|LOG_CONS" LOGFAC="LOG_DAEMON" LOGSET=-DLOGOPT=${LOGOPT} -DLOGFAC=${LOGFAC} LANG=en CC=gcc SYSINS=install #CFLAGS=-Wall -O3 -march=i686 ${LOGSET} CFLAGS=-Wall -O2 $(LOGSET) #ifdef DEMIME :) #LDFLAGS=-L. -lripmime -lpcre -lssl -lcrypto #-static #else LDFLAGS=-L. -lpcre -lssl -lcrypto #endif :) PREFIX=/usr DESTDIR= #MANDIR=$(PREFIX)/share/man/man8 MANDIR=$(PREFIX)/man/man8 piddir=/var/run/$(PROGS) datadir=/var/spool/$(PROGS) notify=/var/spool/$(PROGS)/notify userdir=/etc/$(PROGS) docdir=/usr/doc/$(DISTNAME) user=mail:mail # End user options OBJECTS=getlinep3.o getline_ssl.o parsefile.o p3scan.o \ scanner_basic.o scanner_avpd.o scanner_avpd_new.o \ scanner_trophie.o scanner_clamd.o scanner_bash.o #LIBS=libripmime.a all: $(PROGS) #ripmime/libripmime.a: # cd ripmime && $(MAKE) libripmime || exit 1 #libripmime.a: ripmime/libripmime.a # ln -sf ripmime/libripmime.a libripmime.a p3scan: $(OBJECTS) $(LIBS) $(CC) -o $@ $(OBJECTS) $(LDFLAGS) .c.o: @# generic for all c .o $(CC) $(CFLAGS) -c $< dep depend .dep: @echo "creating depencies" rm -f .tmp.dep @find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0rt $(CC) -M $(CFLAGS) >>.tmp.dep mv .tmp.dep .dep install: p3scan $(SYSINS) -v -m 550 --strip p3scan $(PREFIX)/sbin/ @if [ -f $(DESTDIR)/etc/sysconfig/init ] ; then $(SYSINS) -v -m 755 p3scan-init.d $(DESTDIR)/etc/init.d/p3scan; fi @if [ -f $(DESTDIR)/etc/rc.d/rc.p3scan ] ; then \ echo "rc.p3scan already exists, copying to $(DESTDIR)/etc/rc.d/rc.p3scan.new" ; \ $(SYSINS) -v -m 660 rc.p3scan $(DESTDIR)/etc/rc.d/rc.p3scan.new ; \ else \ $(SYSINS) -v -m 755 rc.p3scan $(DESTDIR)/etc/rc.d ; \ fi @if test -d $(DESTDIR)$(piddir); then echo "$(DESTDIR)$(piddir) exists, not creating."; else mkdir -p $(DESTDIR)$(piddir); fi @if test -d $(DESTDIR)$(userdir); then echo "$(DESTDIR)$(userdir) exits, not creating."; \ else mkdir -p $(DESTDIR)$(userdir); chown $(user) $(DESTDIR)$(userdir); fi @if [ -f $(DESTDIR)$(userdir)/p3scan.conf ] ; then \ echo "$(DESTDIR)$(userdir)/p3scan.conf already exists, copying to $(DESTDIR)$(userdir)/p3scan.conf.sample" ; \ $(SYSINS) -v -m 600 p3scan.conf $(DESTDIR)$(userdir)/p3scan.conf.sample ; \ else \ $(SYSINS) -v -m 600 p3scan.conf $(DESTDIR)$(userdir)/ ; \ fi @if [ -f $(DESTDIR)$(userdir)/p3scan.mail ] ; then \ echo "$(DESTDIR)$(userdir)/p3scan.mail already exists, renaming to $(DESTDIR)$(userdir)/p3scan.mail.old" ; \ mv $(DESTDIR)$(userdir)/p3scan.mail $(DESTDIR)$(userdir)/p3scan.mail.old ; \ fi ln -sf $(DESTDIR)$(userdir)/p3scan-$(LANG).mail $(DESTDIR)$(userdir)/p3scan.mail @if test -d $(DESTDIR)$(datadir)/children; then echo "$(DESTDIR)$(datadir)/children exists, not creating."; else mkdir -p $(DESTDIR)$(datadir)/children; fi @if test -d $(DESTDIR)$(datadir)/notify; then echo "$(DESTDIR)$(datadir)/notify exists, not creating."; else mkdir $(DESTDIR)$(datadir)/notify; fi @chown $(user) $(DESTDIR)$(piddir) @chown -R $(user) $(DESTDIR)$(datadir) @chmod -R 700 $(DESTDIR)$(datadir) $(SYSINS) -v -m 644 p3scan.8.gz $(DESTDIR)$(MANDIR) $(SYSINS) -v -m 644 p3scan_readme.8.gz $(DESTDIR)$(MANDIR) @if test -d $(DESTDIR)$(docdir); then echo "$(DESTDIR)$(docdir) exists, not creating."; else mkdir -p $(DESTDIR)$(docdir); fi $(SYSINS) -v -m 644 AUTHORS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 CHANGELOG $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 CONTRIBUTERS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 LICENSE $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 LICENSE.OpenSSL $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 NEWS $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README-ripmime $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 README-rpm $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 spamfaq.html $(DESTDIR)$(docdir) $(SYSINS) -v -m 644 TODO.list $(DESTDIR)$(docdir) # Add translated mail files here: $(SYSINS) -v -m 644 p3scan-??-??.mail $(DESTDIR)$(userdir) $(SYSINS) -v -m 644 p3scan-??.mail $(DESTDIR)$(userdir) clean: cd ripmime && $(MAKE) clean rm -f ripmime/libripmime.a rm -f ripmime/ripOLE/ripole rm -f $(DISTNAME).tar.* $(PROGS) $(OBJECTS) $(LIBS) .dep core uninstall: @if [ -f $(PREFIX)/sbin/$(PROGS) ] ; then rm -f $(PREFIX)/sbin/$(PROGS) ; fi @if [ -f /etc/init.d/p3scan ] ; then rm -f /etc/init.d/p3scan ; fi @if [ -f /etc/rc.d/rc.p3scan ] ; then rm -f /etc/rc.d/rc.p3scan ; fi @if [ -f $(MANDIR)/p3scan.8.gz ] ; then rm -r $(MANDIR)/p3scan.8.gz ; fi @if [ -f $(MANDIR)/p3scan_readme.8.gz ] ; then rm -r $(MANDIR)/p3scan_readme.8.gz ; fi @if [ -f $(userdir)/p3scan.conf.sample ] ; then rm -f $(userdir)/p3scan.conf.sample ; fi @if [ -f $(userdir)/p3scan.mail.sample ] ; then rm -f $(userdir)/p3scan.mail.sample ; fi @if [ -d $(docdir) ] ; then rm -rf $(docdir) ; fi @if [ -f $(MANDIR)/p3scan.8.gz ] ; then rm -f $(MANDIR)/p3scan.8.gz ; fi @if [ -f $(MANDIR)/p3scan_readme.8.gz ] ; then rm -f $(MANDIR)/p3scan_readme.8.gz ; fi @{ test ! -d $(datadir) || { find $(datadir) -type d ! -perm -700 -exec chmod u+w {} ';' && rm -fr $(datadir); }; } @{ test ! -d $(piddir) || { find $(piddir) -type d ! -perm -700 -exec chmod u+w {} ';' && rm -fr $(piddir); }; } @echo "$(PROGS), $(datadir), and samples removed." @if [ -f $(userdir)/p3scan.conf ] ; then echo "$(userdir)/p3scan.conf remains." ; fi @if [ -f $(userdir)/p3scan.mail ] ; then echo "$(userdir)/p3scan.mail and p3scan-??.mail remains." ; fi tags: @#VIM Users know why! *g* ctags --c-types=+c+p+f+x -R . fulltags: @#VIM Users know why! *g* find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0r $(CC) -M -H $(CFLAGS) 2>.totag >/dev/null find -maxdepth 1 -name "*.c" -print0 | xargs -n 1 -0r echo >>.totag cat .totag | sed "s/^[[:space:]]*//" | grep -v "^$(CC)" | sort | uniq >.totag ctags --c-types=+c+p+f+x -L .totag . rm -f .totag dist: rm -rf $(DISTNAME) mkdir $(DISTNAME) $(MAKE) clean cp -prd Makefile* getline*.{c,h} parsefile.{c,h} p3scan.{c,h} $(DISTNAME) cp scanner.h scanner_*.c $(DISTNAME) cp p3scan.conf p3scan-??.mail p3scan-??-??.mail rc.p3scan $(DISTNAME) cp p3scan.8.gz p3scan_readme.8.gz $(DISTNAME) cp rc.p3scan p3scan-init.d p3scan.sh $(DISTNAME) cp *.patch $(DISTNAME) cp AUTHORS CHANGELOG CONTRIBUTERS LICENSE LICENSE.OpenSSL NEWS README README-ripmime README-rpm spamfaq.html TODO.list $(DISTNAME) cp -prdv ripmime-$(RIPMIME) $(DISTNAME) cp -rv ripmime $(DISTNAME) rm -f $(DISTNAME).tar.gz* tar cfv $(DISTNAME).tar $(DISTNAME)/ rm -rf $(DISTNAME) gzip -9f $(DISTNAME).tar md5sum $(DISTNAME).tar.gz > $(DISTNAME).tar.gz.md5 @echo "Now create signature with:" @echo "gpg -a -o $(DISTNAME).tar.gz.asc -b $(DISTNAME).tar.gz" # gpg -a -o $(DISTNAME).tar.gz.asc -b $(DISTNAME).tar.gz .PHONY: clean install all tags fulltags dist dep depend #include .dep p3scan-2.3.2/scanner_trophie.c0000644000175000001440000001362010347310635015076 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "p3scan.h" #define DEFAULT_SOCKET_PATH "/var/run/trophie" extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); struct configuration_t * config; static int trophie_fd; // fd for log static int connected; // have done connect static struct sockaddr_un trophie_socket; // AF_UNIX address of local logger static int trophie_socket_connect(struct proxycontext *p){ if (trophie_fd == -1){ bzero((char *)&trophie_socket, sizeof(trophie_socket)); trophie_socket.sun_family=AF_UNIX; strcpy(trophie_socket.sun_path, config->virusscanner); if ((trophie_fd=socket(AF_UNIX,SOCK_STREAM,0)) < 0 ){ do_log(LOG_CRIT, "create socket error: socket() not created %s", config->virusscanner); return -1; } } if (trophie_fd!=-1 && connected==-1){ do_log(LOG_DEBUG, "Trying to connect to socket"); if (connect(trophie_fd, (struct sockaddr *)(&trophie_socket), sizeof(trophie_socket.sun_family) + strlen(config->virusscanner)) >= 0){ connected=1; do_log(LOG_DEBUG, "trophie_socket_connect connected"); return 0; } } else { do_log(LOG_DEBUG, "Already connected"); return 0; } do_log(LOG_CRIT, "can't connect to socket %s", config->virusscanner); return -1; } static void trophie_socket_close(void){ close(trophie_fd); trophie_fd=-1; connected=0; do_log(LOG_DEBUG, "trophie_socket_close"); } static int trophie_scanfile(struct proxycontext * p, char * filetoscan, char ** virname){ char *sendbuf; char recvbuf[512]; int len; *virname=NULL; if(trophie_fd<0 || !connected) if (trophie_socket_connect(p)!=0) return SCANNER_RET_ERR; len=strlen(filetoscan); //--sendbuf=malloc(len+2); sendbuf=w_malloc(len+2); (void)snprintf(sendbuf, len+2, "%s\n", filetoscan); /* send filename */ do_log(LOG_DEBUG, "Sending to socket"); if (write(trophie_fd, sendbuf, len+1) <0){ do_log(LOG_ALERT, "Can't write to trophie socket"); //--free(sendbuf); w_free(sendbuf); return SCANNER_RET_ERR; } //--free(sendbuf); w_free(sendbuf); do_log(LOG_DEBUG, "OK"); /* retrieve message */ memset(recvbuf, 0, sizeof(recvbuf)); if ((len = read(trophie_fd, recvbuf, sizeof(recvbuf))) > 0){ do_log(LOG_DEBUG, "%i bytes read", len); if (strchr(recvbuf, '\n')) *strchr(recvbuf, '\n') = '\0'; if (recvbuf[0] == '1'){ /* virus */ do_log(LOG_DEBUG, "it's a virus"); *virname=strdup(recvbuf+2); return SCANNER_RET_VIRUS; } else if (!strncmp(recvbuf, "-1", 2)){ do_log(LOG_CRIT, "Error scanning %s (error or file not found)", filetoscan); return SCANNER_RET_ERR; } } else { do_log(LOG_ALERT, "Can't read message to trophie socket"); return SCANNER_RET_ERR; } return SCANNER_RET_OK; } static int init1(void){ do_log(LOG_DEBUG, "Trophie Init1"); if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. we're using " DEFAULT_SOCKET_PATH); config->virusscanner=strdup(DEFAULT_SOCKET_PATH); } connected=-1; trophie_fd=-1; do_log(LOG_DEBUG, "Trophie Init1 Done"); return 0; } static int init2(struct proxycontext *p){ do_log(LOG_DEBUG, "Trophie Init2"); /* Connect to socket */ if (trophie_socket_connect(p)!=0) return -1; do_log(LOG_DEBUG, "Trophie Init2 Done"); return 0; } static void uninit2(struct proxycontext *p){ trophie_socket_close(); } static int scan(struct proxycontext *p, char ** virname){ int ret; do_log(LOG_DEBUG, "Trophie scanner says hello"); ret=trophie_scanfile(p, p->scanthis, virname); do_log(LOG_DEBUG, "Trophie scanner says goodbye"); return ret; } scanner_t scanner_trophie = { "trophie", /* name */ "Trophie antivirus daemon (for Trend Antivirus)", /* description */ &init1, /* init1 (once, afer startup) */ &init2, /* init2 (every connection before first mail) */ &scan, /* scan */ &uninit2, /* uninit2 */ NULL, /* uninit1 */ 0 /* dirscan */ }; p3scan-2.3.2/scanner_basic.c0000644000175000001440000001463510347310635014514 0ustar jlaiusers/* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ #include #include #include #include #include #include #include #include "p3scan.h" extern int checkbuff(int fdc); extern int checktimeout(struct proxycontext *p); struct configuration_t * config; extern void * w_malloc(size_t bytes); extern void w_free(void *f_address); static int scan(struct proxycontext *p, char ** virname){ int ret,ret2,fdc; char * command; int len; FILE * scanner; static char line[4096*16]; pcre * rx; const char *pcre_error; int pcre_erroffset; int offsets[50]; #define VISIZE 1000 char *vi=w_malloc(VISIZE); int vipos = 0; int vcodndx; do_log(LOG_DEBUG, "Basic scanner says hello"); if (vi==NULL) return SCANNER_RET_ERR; if (config->virusregexp){ rx = pcre_compile(config->virusregexp, PCRE_UNGREEDY /* | PCRE_CASELESS */ , &pcre_error, &pcre_erroffset, NULL); if (!rx) { /* should not happen, because init1 has already tested and set to NULL on error */ do_log(LOG_WARNING, "Ouch! Can't compile regular expression: %s (char %i)", pcre_error, pcre_erroffset); } } else { rx=NULL; } len=strlen(config->virusscanner) + strlen(p->scanthis) + 1 + strlen(" '' 2>&1"); command=w_malloc(len+1); snprintf(command, len, "%s '%s' 2>&1", config->virusscanner, p->scanthis); do_log(LOG_DEBUG, "popen %s", command); if ((scanner=popen(command, "r"))==NULL){ do_log(LOG_ALERT, "Can't start scanner '%s' !!!", command); w_free(vi); w_free(command); return SCANNER_RET_ERR; } fdc=fileno(scanner); ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; while (!ret2){ ret2=checkbuff(fdc); if (ret2 > 1) return SCANNER_RET_CRIT; ret=checktimeout(p); if (ret < 0) return SCANNER_RET_CRIT; } vi[0]='\0'; *virname=vi; while ((fgets(line, 4095, scanner))!=NULL){ line[strlen(line)-1]='\0'; #ifdef DEBUG_SCANNING do_log(LOG_DEBUG, "ScannerLine: '%s'", line); #endif if (rx){ ret = pcre_exec(rx, NULL, line, strlen(line), 0, 0, offsets, 50); if (ret > config->virusregexpsub){ len=pcre_copy_substring(line, offsets, ret, config->virusregexpsub, vi+vipos, VISIZE - vipos -4 ); if (len==PCRE_ERROR_NOMEMORY) break; if (len>0) vipos+=len; vi[vipos]=' '; vipos++; vi[vipos]='&'; vipos++; vi[vipos]=' '; vipos++; } } } ret=pclose(scanner); //--free(command); w_free(command); if (vipos > 3) vi[vipos-3]='\0'; do_log(LOG_DEBUG, "vi : '%s'", vi); if (rx) pcre_free(rx); if (!WIFEXITED(ret)){ do_log(LOG_ALERT, "Scanner returned abnormal signal (%i)", ret); return SCANNER_RET_ERR; } else do_log(LOG_DEBUG, "Scanner returned signal %i", WEXITSTATUS(ret)); ret=WEXITSTATUS(ret); for (vcodndx = 0; vcodndx < MAX_VIRUS_CODES && config->viruscode[vcodndx] != -1; vcodndx++) { if (ret == config->viruscode[vcodndx]) return SCANNER_RET_VIRUS; /* contains a virus */ } for (vcodndx = 0; vcodndx < MAX_VIRUS_CODES && config->gvcode[vcodndx] != -1; vcodndx++) { if (ret == config->gvcode[vcodndx]){ ret = 0; do_log(LOG_DEBUG, "Basic scanner says goodbye (goodcode)"); return SCANNER_RET_OK; /* good return code */ } } if (ret!=0){ do_log(LOG_ALERT, "WARNING: Your scanner returned neither 0, a viruscode, nor a good viruscode, but %i", ret); return SCANNER_RET_ERR; } do_log(LOG_DEBUG, "Basic scanner says goodbye"); return SCANNER_RET_OK; /* all ok, no virus */ } static int init1(void){ pcre * rx; const char *pcre_error; int pcre_erroffset; if (strlen(NONULL(config->virusscanner))<1){ do_log(LOG_CRIT, "no scanner was defined. scanning completely disabled"); return SCANNER_RET_ERR; } if (strlen(NONULL(config->virusregexp))>0){ rx = pcre_compile(config->virusregexp, PCRE_UNGREEDY /* | PCRE_CASELESS */ , &pcre_error, &pcre_erroffset, NULL); if (!rx) { do_log(LOG_WARNING, "Can't compile regular expression: %s (char %i). Virusnames can't be extracted", pcre_error, pcre_erroffset); config->virusregexp=NULL; } else { do_log(LOG_DEBUG, "RX compiled succesfully"); pcre_free(rx); } } else { config->virusregexp=NULL; do_log(LOG_WARNING, "No Regular Expression given! Virusnames can't be extracted"); } return 0; } scanner_t scanner_basic = { "basic", /* name */ "Basic file invocation scanner", /* description */ &init1, /* init1 (once, afer startup) */ NULL, /* init2 (every connection before first mail) */ &scan, /* scan */ NULL, /* uninit2 */ NULL, /* uninit1 */ 1 /* dirscan */ }; p3scan-2.3.2/p3scan.8.gz0000644000175000001440000000614110347310635013446 0ustar jlaiusersЍmCp3scan.8Yo7} w@l^RNҨ%)@RRp}3ܗpXrf873T'jㅬɥ׉̲XBYU*[7u.Ww(BWBYCMr/gzݖL^[l*$CF\ NUQ5 =H[0 TD/jVN_ī@tH$?}ݚ&_GS. MKty:FWڡ!ph*NFwAu,ƿFK _`rBqίj D?U;ʫg=qĐKτAS"F]G(mjϨ_ ZB~DCznws`aQT¿6ELj 1CFQQh'W׹|p:̣[xw6s&!RfZEC-V9fճ,3\=r*1Xߋyq%L]'\1Mɔ\g0D=M8 ::j}Eyz9K.R+E-ANLJҢx]B5 .vdvsT&C09XbRS L vF%7qK p\68RXbDZplIݙJ_6˱xvNBO@1ZƆzzɉF%*|KglMvmG96Z7b(g:\ެDlH}'wʸԽn'Cb|R O3(:q󌸡gRuvu&{df;+غjA·͹BqjflouxQd"qB޵=ЩO bPozEfUI&"GB,ɱ8oG}sT98`XUGR^ݹ%KΚg*:̳ߎ.KF7bj+Nu?ф CՒPz_0 {]SI@T,:e-9d!ZT?H%Q7w8l M_;{@˝ }t‰G5NCzxCVҖPk" ULͲ*ڞc ] ػό8nv3a❾*nx֔U@ MǴ T@S@q34\ތnh8YԿuӐ;h=Y4ZN?LnfϬpeVϭ7YQWx6|fxsngo.8d~{a}f>3rKWy7M- mFO&UX=5 R1U6,bAwy* mg;T&]+cV( Vߢ*M?vՃ̶]x|A7M(mlBVriv@S)bܶctR{9F6|rа+4Ə9ڧB tⶺ7fT3W2;8n\h_q (ݠJ}; .xSs;·S]6iE?ђ7 N ,a4\UvAU#B*ٛT{"@])8{)#|`I Ʃ ҙ-iI*&Ʀ=bW7d;D.jfQM$nAF'$_wɎ/oX?}345^< ՘eӐrIxy0Lr#eLG鮒zavtR}2W{JC4~{Ml6٤iv*7OE>)`v;YMCoש-kN[ W0b!{}Uh8Z{_P , RNLY&wb+I&OV.1ӏi"B~[';Dc~ #Wp3scan-2.3.2/p3scan.conf0000644000175000001440000005266410347310635013620 0ustar jlaiusers########################################################################## # # # P3Scan Version 2.3.2 # # # # default configuration file # # all params are set to default # # # ########################################################################## /* * P3Scan v2.3.2 * * (C) 2003-2005 by Jack S. Lai * * It's intent is to provide a follow on program to POP3-Virusscan-Proxy 0.4 * by Folke Ashberg . * * It is based upon his program but provides numerous changes to include * scanning pop3 mail for spam, hardening the program, addaption to todays * email environment, and many other changes. * * The initial release of p3scan includes patches made and submitted to the * original project but were never incorporated. Please see the README for * further information. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This program is released under the GPL with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed." * (http://www.openssl.org/support/faq.html#LEGAL2) * */ # # PID File # # where to write a pid-file # # default: /var/run/p3scan/p3scan.pid # # pidfile = /var/run/p3scan/p3scan.pid # # Max Child's # # The maximum number of connections we will handle at once. Any further # connections will be dropped. Keep in mind that a number of 10 also # means that 10 viruscanners can run at once. # # default: 10 # # maxchilds = 10 # # IP Address # # The IP Address we listen on default: 0.0.0.0 (any address) # # ip = 0.0.0.0 # # Port # # The tcp port on we should listen. If you need a privileged port you # need to start p3scan as root (but don't set username to root, # that's not necessary, because first after opening the port we will # switch to that user). # # default: 8110 # # port = 8110 # # TargetIP, TargetPort # # targetip and targetport are the ip and port to connect - # default is 0.0.0.0 (transparent proxy mode) and 8110 respectively # # default: targetport is ignored in transparent proxy mode # # targetip = 0.0.0.0 # targetport = 8110 # # Useurl # # Parse username for destination url vice using iptables redirection. # # To use this you should have your email clients replace the username # in the email client username field with "username#destinationurl:port" # and replace the destination url/port with the url/port of p3scan. # # Example: # From: # username: laitcg host: pop.gmail.com port: 110 # To: # username: laitcg#pop.gmail.com:110 host: port: 8110 # # default: # # useurl # # Email (SMTP) Port # # The port we should listen on to scan outgoing email messages. # # default: 25 # # emailport = 25 # # Username # # The username the daemon should run as. Takes no effect when you # start as a non-root user. # # default: mail # # user = mail # # Notify Directory # # Create notification mails in . Also used for temporary storage. # # default: /var/spool/p3scan/notify # notifydir = /var/spool/p3scan/notify # # Virus Directory # # The directory in which infected mails will be stored. It is also # used for temporary storing. Ensure that the above specified user is # allowed to write into! # # default: /var/spool/p3scan # # virusdir = /var/spool/p3scan # # Just Delete # # Instead of keeping an infected message in the Virus Directory, delete # it after reporting it to the user. # # default: Keep infected messages in Virus Directory # # justdelete # # Extra Notification Recipient # # When an infection message is sent to the client, send a copy of the # message to the address(s) specified here, separated by a space. # # NOTE: If there is a file named "p3scan.extra" it will be treated just # like p3scan.mail (see "template") and sent to the recipients listed here. # For example: # /etc/p3scan/p3scan.mail -> /etc/p3scan/p3scan-en.mail # /etc/p3scan/p3scan.extra -> /etc/p3scan/p3scan-ru.mail # # default: Do not notify anyone else # # extra = root@localhost postmaster@localhost # # Extra Notification Recipient mail program # # Use this program to send Extra Notification. # # default: /bin/mail # # xmail = # # Bytes Free # # The number of KB's there must be free before processing any mail. # If there is less than this amount, p3scan will notify the emergency # contact address and then terminate itself. # # NOTE: p3scan could need (2 * msgsize) * children disk space free. # Being this is dynamic (not all space is needed all the time), # you should ensure you have more than enough disk space. # # default: bytesfree = 10000 (10MB) # Sample: If you want to ensure 100MB are free # bytesfree = 100000 # # Emergency Contact # # In the event p3scan encounters a catastrophic problem and has to terminate, # it will send an email to these email addresses just before setting up to # close down on the next iteration of a child process. # # default: root@localhost postmaster@localhost # # emergcon = # # Scanner Type # # Select here which type of scanner you want to use. # # Basic: # # This is the default. The configured executable (set in variable # "scanner") will be invoked. You can also specify parameters (we are # using /bin/sh). At the end the path to the mail and a "2>&1" is # appended. The program can tell us if it's a virus returning Scanner # Returncode (see below) or exit code 0 means, with all ok, all # others are reported to syslog, but mails will be delivered unless # justdelete is enabled above. The output is scanned using a regular # expression which describes where the virusname can be found # (see virusregexp). # # If demime is not set 'path to mail' is the full filename to the # rfc822 message, which you MUST NOT DELETE or MODIFY (except if you know # how to modify)! If your scanner can not handle rfc822 messages (e.g. # McAfee uvscan) set demime and 'path to mail' is a directory which # contains all MIME-Parts splitted into separate files. That files are # not needed after scanning, so p3scan deletes them. # # You will find a sample-configuration for McAfee's uvscan # (http://www.mcafee.com/) in the below sections. # # # AVPD: # # AVPD is a frontend to 'Kaspersky Anti-Virus for Linux' # (http://www.kaspersky.com/) , which provides a daemon named # 'kavdaemon'. Once the daemon has been started we connect to # kavdaemons socket and tell what files to scan. This gives a rapid # speed increase since the virus-definitions and other # scanner-initialization has only to be done once! Here a result from # a speedtest which I've made: Running kavdaemon, quallcomms qpopper # and p3scan on an Intel P1 with 133MHz and 64MB RAM a client using # Netscape-Mail was able to fetch 500 mails (including POP3- UIDL- # setting) in 62 seconds (each mail sized 2kByte)! # # We just need to know in which directory your kavdaemon writes the # socket 'AvpCtl' and file 'AvpPid', default is '/var/run', which is # ok using AVP-default installation, leave in that case the below # variable scanner commented, otherwise set there the path. Parameter # viruscode and virusregexp will not be used. # # Actual versions of avpd can't handle rcf822, so set demime (this is # not standard, it's possible that Kaspersky Labs includes rfc822 # checking in further releases). # # Important note about using kavdaemon: # # Ensure that virusdir (/var/spool/p3scan) is included in AVPs # 'enabled path list', otherwise the mails will not be scanned, but # kavdaemon returns ok (I've found no way to check if the mail has # been scanned or not). Mail yourself the eicar.com testvirus (from # http://www.eicar.com ) to check it!). The 'enabled path' can be set # in /opt/AVP/defUnix.prf in the 'Names' line (add # ';*/var/spool/p3scan'). Also check if there is a config file in # /root/.AVP/ which has higher precedence (if kavdaemon runs a root, # otherwise check that users $HOME/.AVP ). # scannertype = avpd # # If you are using version 5, then instead of scannertype = avpd, use # scannertype = avpd_new # # TROPHIE: # # Trophie is an OpenSource Anti-Virus Daemon, which uses the # virus-scanengine and database from Trend Antivirus. Trophie can be # found at http://www.vanja.com/tools/trophie/ . Configuration is very # simple, just set scannertype to 'trophie' and it should work. If you # don't use trophies standard-config you have to set scanner to the # trophie socket. # scannertype = trophie # # FRISK F-Prot Antivirus: http://www.f-prot.com # # Un-comment appropriate options below. # Use default scannertype = basic # # Clam Anti-Virus: http://www.clamav.net # # This program must run as the same user as p3scan is running so that # it can access the mail files for scanning. Either compile with the # options --with-user=mail --with-group=mail (if p3scan is using the # the default user/group of "mail") or change "User" in clamav.conf # to the user p3scan is running as. If you get a return code other # than a 0 or 1, see the clamav documentation for the reason. # # Un-comment "ScanMail" in clamav.conf/clamd.conf as we are scanning mail # files so demime does not need to be set below. # # You should start clamd before starting p3scan. # # Un-comment appropriate options below. # Use default scannertype = basic # # If using the tcp/ip mode of clamd, then use scannertype = clamd # This will allow using the clamav scanner even when it is not running on # the same server as p3scan. # # Bash # # This scanner type allows you to do whatever you want with the # variables passed to it and can also allow you to call multiple # scanners, etc... See p3scan.sh for an example of all the variables. # scannertype = bash # # default: basic # # scannertype = avpd # scannertype = avpd_new # scannertype = bash # scannertype = basic # scannertype = clamd # scannertype = trophie # Virusscanner # # Depends on scannertype. Read the above section of that scannertype # you're going to use and you do not need to ask what to fill in here. # # default: depending on scannertype: # basic : # bash: : /path/to/filename # avpd : /var/run/ # avpd_new: /var/run/ # trophie : /var/run/trophie # clamd : tcp/ip connection # # # Sample: scannertype basic using McAfee UVSCAN: # scanner = /usr/local/uvscan/uvscan # Sample: scannertype basic using FRISK F-Prot Antivirus: # scanner = /usr/local/bin/f-prot -archive=5 -ai # Sample: scannertype basic using ClamAV: # scanner = /usr/bin/clamdscan --no-summary # Sample: scannertype clamd using ClamAV in TCPSocket mode: # scanner = 127.0.0.1:3310 # Sample: scannertype bash using p3scan.sh for testing: # scanner = /usr/local/sbin/p3scan.sh # # Scanner Returncode # # Specify the returncode(s) which the scanner returns when the mail is # infected. P3Scan does its part (sending the notification and not the # infected mail) only when it gets the specified returncode(s). # A returncode value of 0 from the scanner is assumed to mean that the # message is clean. Any other unspecified value will add warning lines # to your logfiles but THE MESSAGE WILL BE DELIVERED! # # Only used from scannertype 'basic'. # # default: 1 # # Sample: scannertype basic using McAfee UVSCAN: # viruscode = 13 # Sample: scannertype basic using FRISK F-Prot Antivirus: # viruscode = 3,8 # # Good Scanner return codes # # Some scanners can report more than good or infected. Place valid return # codes here that will enable the message to be delivered without a # warning. For example, Kaspersky Anti-Virus reports code 10 for an # encrypted .zip file. # # default: none # Sample: goodcode = 10 # goodcode = # # Regular Expression for Virusname # # Specify here a regular expression which describes where the name of the # virus can be found. If not specified, the first substring is used; # specify it appending '/' and the substring number (1-9) at the end. # PerlCompatibleRegularExpressions are used, case sensitive and the # ungreedy option. Only used by scannertype 'basic'. # # default: # # Sample: McAfee UVSCAN # virusregexp = ^[[:space:]]*Found( the|:)[[:space:]]*(.*)[[:space:]]*(|virus[^a-z.]*)$/2 # Sample: FRISK F-Prot Antivirus # virusregexp = (?=Infection\:)[[:space:]]*(.*)$ # Sample: ClamAV # virusregexp = .*: (.*) FOUND # # deMIME Setting # # Uncomment this if we should parse all MIME-sections instead of passing # the message as-is to the scanner. This is used for viruscanners that cannot # natively handle email attachments. # # default: # demime # # Broken email clients # # Some email clients may require special processing. If this # option is enabled the client will receive whole lines (vice # single characters) while processing a large email message. # # The reason for leaving two types of large email processing # is that when this option is disabled, the possibility exists # that the client will not see any "X-P3Scan: Due to an # extremely large attachment you see this message line." in # the received message header. # # Note: As of the introduction of this parameter, only some # instances of the use of the Outlook/Outlook Express clients # warrent enabling of this feature. # # default: send characters during large message processing. # broken # # Timeout # # Change the default timeout for sending characters/lines to the # client while processing a message. # # default: 30 seconds # timeout = 30 # # ISP Spam # # This option allows you to set the string your ISP uses if # it processes your email for SPAM. If this string is found # in the subject line of the incoming message, it will not # perform any spam processing so that the message gets to # the client faster. # # default: # Sample for cox.net: # ispspam = -- Spam -- # # ispspam = # Enable Spam checking # # If set, will scan for Spam before scanning for a virus. # # P3scan has been tested with both dspam-3.0.0-rc2 and # Mail::SpamAssassin v2.6. # # The DSPAM implementation uses the virtual-users capability # of the mysql backend to dspam. Mysql is the recommended # interface due to speed and stability (and in our case, the # virtual-users interface). # # P3scan and SpamAssassin uses the interface spamd/spamc. # You should start spamd before running p3scan. For example: # "spamd -L -d" (run in local mode only, daemonize) # man spamd for more information. # # default: no checking of spam # checkspam # # Mail::SpamAssassin # # Where to find spamc, the link to the Mail::SpamAssassin daemon spamd. # # spamcheck = /usr/bin/spamc # # DSPAM # # This line gives SpamAssassin like functionality to dspam. All # users mail is scanned against a single database of "mail". #spamcheck = /usr/bin/dspam --user mail --mode=teft --stdout --deliver=innocent,spam --feature=ch,no,wh # # This line enables each virtual user to have their own database # of message traffic 'tokens' and is much more accurate than the # procedure above after training. The user 'dspamuser' will be # replaced by the actual user calling p3scan. #spamcheck = /usr/bin/dspam --user dspamuser --mode=teft --stdout --deliver=innocent,spam --feature=ch,no,wh # # Of course change the options in the above lines to your preferences. # # Rename Attachments # # If renattach is installed and this option is un-commented, we # will execute renattach to rename dangerous attachments. # (See README for more information) # # default: none # # renattach = /usr/local/bin/renattach # # Overwrite (disable) HTML # # If a person views an HTML message, not only can the client # download pictures automatically, it enables someone viewing # the remote log file to confirm the email address is valid, # making it "worth" keeping/selling, etc... # # p3scan comes with a separate program p3pmail that can be # installed for this purpose. # # default: do not disable HTML # # overwrite = /usr/bin/p3pmail # # Debug # # Turn on debugging. The recommended debug procedure is to # call p3scan as follows: # p3scan -d > debug 2>&1 # This will trap debug information to the file "debug". # You can then monitor it with a 'tail' command. # # default: off # debug # # Quiet # # Disable reporting of normal operating messages. Only report errors # or critical information. # # default: display all less debug info # # quiet # # Template # # Where to look for an email-template when our own mail has to be send # instead of an infected mail. That file has to be exist, otherwise # p3scan will send an RFC unconform -ERR and closes the connections. # The email-template should start after the header with the content-type, and # so on. Also the leading dot is necessary (just a dot and return, no more in # the last line). You can use some key- words which will be replaced # when sending, e.g. %MAILDATE%. See the existing p3scan-en.mail for examples # of keywords. # # p3scan.mail is normally a sym-link to your default language email template. # You can of course just put the actual name of your template here. # # default: /etc/p3scan/p3scan.mail # # template = /etc/p3scan/p3scan.mail # # Alternate virus notification email # # In the event you wish to modify the virus notification email on the # fly, as in using the bash scanner, uncomment this to allow a copy of the # p3scan.mail virus template to be placed in: # $virusdir//vnmsg or /var/spool/p3scan/children//vnmsg # # default: use template defined in template= w/out modification. # # altvnmsg # # Subject # # This option can be used to change the default subject line when reporting # a virus infected message. In the default below, everything between the quotes # can be changed and are not part of the actual default subject line and # will be replaced by the actual name of the detected virus. # # default: Subject: "[Virus] found in a mail to you:" # # subject = # # Notify # # This option can be used to change the default file deleted notification that # is displayed in the virus notification message when the "justdelete" option # is used. # # default: Per instruction, the message has been deleted. # # notify = # # SMTP Reject # # This option can be used to change the default SMTP Reject message that is sent # to the client in the event a message is rejected due to a virus. # The error message will have a prefix of "554". # # default: Virus detected! P3scan rejected message! # # smtprset = # # Check SMTP size # # This option can be used to set the maximum message size (in KBytes) that # p3scan will use to determine if it should scan an smtp submission. If a # message is greater than this size, it will not be scanned to help alleviate # smtp timeouts. # # default: none # # checksize = # # SSL/POP3S messages # # Check for SSL/POP3S protocol on a different port. # # default: 995 # # sslport = # # Insert SMTP message virus scanner information. # # This option is used to add the virus definition info from your scanner # to an smtp message. It will only be added as a footer if the message # is not signed cryptographically and is only a text message. # # It is used in conjunction with the file /etc/p3scan/p3scan.footer in the # following fashion: # # /etc/p3scan/p3scan.footer: # # 1) If file does not exist and "footer" is defined: # No footer information will added to outgoing messages, but the p3scan # version and scanner info will be added to the header. # # 2) If file exists but blank and "footer" is defined: # P3Scan version/host info and scanner info will be added to end of # message and header. # # 3) If file contains information and "footer" is defined: # All lines of this file will be added to the end of the smtp message and # then p3scan version/host info and scanner info will be appended. # # 4) If file does not exist and "footer" is not defined: # P3Scan will only insert p3scan version info into the header. # # Sample: FRISK F-Prot Antivirus: # footer = /usr/local/bin/f-prot -verno # Sample: ClamAV: # footer = /usr/bin/clamdscan -V # # TOP command processing # # This command may cause p3scan to hang when the client does not # disconnect after using the TOP command. It is now disabled by # default. If it was working for you in the past, then go ahead # and enable it below. # # NOTE: This command will be removed in the future as it is only # a work-around until the problem can be resolved. # # enabletop # END of configuration p3scan-2.3.2/p3scan-pl.mail0000644000175000001440000000144310347310635014213 0ustar jlaiusersMIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="iso-8859-2" Witaj %USERNAME%. Ta wiadomo?? zosta?a wygenerowana automatycznie przez program P3Scan, ktry dzia?a na serwerze %HOSTNAME%.%DOMAINNAME% i skanuje poczt? pod k?tem wirusw i spamu. W wiadomo?ci wys?anej do ciebie zosta? znaleziony WIRUS! Ta wiadomo?? zosta?a wys?ana do ciebie zamiast zainfekowanego e-maila. Mo?esz przejrze? ?rd?o tej wiadomo?ci, je?li chcesz zobaczy? nag?wki zainfekowanego emaila. Nazwa wirusa: %VIRUSNAME% (Rzekomy) nadawca: %MAILFROM% Odbiorca: %MAILTO% Data: %MAILDATE% Temat: %SUBJECT% Dane po??czenia: %PROTOCOL% z %CLIENTIP%:%CLIENTPORT% do %SERVERIP%:%SERVERPORT% Plik wiadomo?ci: %P3SCANID% %VDINFO% -- %PROGNAME% %VERSION% by Jack S. Lai