mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35:55 +08:00
draft code for TNEF winmail.dat #1012
This commit is contained in:
parent
8fe5cd3c95
commit
565e8f0fb7
1745
examples/eml/tnef.eml
Normal file
1745
examples/eml/tnef.eml
Normal file
File diff suppressed because it is too large
Load diff
101
examples/eml/tnef2.eml
Normal file
101
examples/eml/tnef2.eml
Normal file
|
@ -0,0 +1,101 @@
|
|||
From: Alice <alice@example.net>
|
||||
To: Bob <bob@example.net>
|
||||
Subject: TNEF test 2
|
||||
Date: Thu, 10 Feb 2022 12:19:57 +0100
|
||||
Message-ID: <001d01d81e70$23211250$696336f0$@example.net>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="----=_NextPart_000_001E_01D81E78.84E7EB50"
|
||||
X-Mailer: Microsoft Outlook 16.0
|
||||
Content-Language: cs
|
||||
X-MS-TNEF-Correlator: 0000000045EA84AB25A1494B8CD618FC27E0940E04CA7500
|
||||
Authentication-Results: example.net;
|
||||
dkim=pass header.d=example.net header.s=mail header.b=UH8A3Z8P;
|
||||
dmarc=pass (policy=reject) header.from=example.net;
|
||||
spf=pass () smtp.mailfrom=example@example.net
|
||||
|
||||
This is a multipart message in MIME format.
|
||||
|
||||
------=_NextPart_000_001E_01D81E78.84E7EB50
|
||||
Content-Type: text/plain;
|
||||
charset="iso-8859-2"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
Dobr=FD den,
|
||||
|
||||
=20
|
||||
|
||||
pos=EDl=E1m zku=B9ebn=ED e-mail:
|
||||
|
||||
=20
|
||||
|
||||
P=F8=EDli=B9 =BElu=BBou=E8k=FD k=F9=F2 =FAp=ECl =EF=E1belsk=E9 =F3dy
|
||||
|
||||
=20
|
||||
|
||||
S pozdravem
|
||||
|
||||
=20
|
||||
|
||||
Mgr. Milo=B9 Bart=E1k
|
||||
|
||||
|
||||
------=_NextPart_000_001E_01D81E78.84E7EB50
|
||||
Content-Type: application/ms-tnef;
|
||||
name="winmail.dat"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment;
|
||||
filename="winmail.dat"
|
||||
|
||||
eJ8+IjoLAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA4gQAAAAAAADmAAEIgAcAGAAAAElQTS5NaWNy
|
||||
b3NvZnQgTWFpbC5Ob3RlADEIAQOQBgCMCgAAJwAAAAsAAgABAAAAAwAmAAAAAAALACkAAAAAAB4A
|
||||
cAABAAAAHAAAAFprb3Waa2EgZS1tYWlsdSBiZXogcPjtbG9oeQACAXEAAQAAABYAAAAB2B5wHowu
|
||||
HckFefFLTYHQP0bV2A3UAAALAAEOAAAAAAIBCg4BAAAAGAAAAAAAAABF6oSrJaFJS4zWGPwn4JQO
|
||||
woAAAAMAFA4BAAAAHgAoDgEAAAAvAAAAMDAwMDAwMDMBYmFydGFrQGVzcC1tYWlsLmN6AWJhcnRh
|
||||
a0Blc3AtbWFpbC5jegAAHgApDgEAAAAvAAAAMDAwMDAwMDMBYmFydGFrQGVzcC1tYWlsLmN6AWJh
|
||||
cnRha0Blc3AtbWFpbC5jegAAAwDeP7BvAAADAPE/BQQAAAMAAlkAABYAAwAJWQIAAAALABCACCAG
|
||||
AAAAAADAAAAAAAAARgAAAAADhQAAAAAAAAMAEoAIIAYAAAAAAMAAAAAAAABGAAAAABCFAAAAAAAA
|
||||
AwBzgAggBgAAAAAAwAAAAAAAAEYAAAAAAYUAAAAAAAALAKmACCAGAAAAAADAAAAAAAAARgAAAAAG
|
||||
hQAAAAAAAAsArYAIIAYAAAAAAMAAAAAAAABGAAAAAA6FAAAAAAAAAwCwgAggBgAAAAAAwAAAAAAA
|
||||
AEYAAAAAGIUAAAAAAAALAMaACCAGAAAAAADAAAAAAAAARgAAAACChQAAAAAAAAMAAIEIIAYAAAAA
|
||||
AMAAAAAAAABGAAAAAOuFAAAFBAAAAgELggggBgAAAAAAwAAAAAAAAEYBAAAANgAAAEkAbgBUAHIA
|
||||
YQBuAHMAaQB0AE0AZQBzAHMAYQBnAGUAQwBvAHIAcgBlAGwAYQB0AG8AcgAAAAAAAQAAABAAAAAW
|
||||
G8UG6IOCQIQ5QUZYFGJYHgAxggggBgAAAAAAwAAAAAAAAEYBAAAAGAAAAEMAbwBtAHAAbwBzAGUA
|
||||
VAB5AHAAZQAAAAEAAAAIAAAAbmV3TWFpbAALAB8OAQAAAAIB+A8BAAAAEAAAAEXqhKsloUlLjNYY
|
||||
/CfglA4CAfoPAQAAABAAAABF6oSrJaFJS4zWGPwn4JQOAwD+DwUAAAACAQkQAQAAAFYGAABSBgAA
|
||||
YREAAExaRnVvWyMSAwAKAHJjcGcxMjWDAtEDYWh0bWwxAzH4YmlkBAADMAEDAfcKgCcCpAPjAgBj
|
||||
aArAc2XgdDIzOCAHEwKAEIPfAFAEVghVB7ISdTIC4xFYtwYABsMSdTMERhFXMBN8VxKDCO8J9zsZ
|
||||
YjUJtDliOQojMTkzEnIMYGNnAFALCQFkMzYXoAulNHIgEIIqXA6yAZAOEDlkIDwOsiB4DtAAgDpI
|
||||
dj0iCHBuOgTwaGJlAMBzLW0N4ANgc2pvAYAtBaBtIAAO0CJdH6VvIC8hOSFgZg3gZXskVSIWdyKv
|
||||
I78loAWwZA0iFm0lwA6wdHA6L6ovJiUuIQcuIaEvJGRiLwHQMDQvDiAqoG0LIfcox3cs4C53My4B
|
||||
BbBnL1RSL1JFhEMtDrI0MCI+EoONHpczHjAfUGVhZC59NDE2DvA8B4ABkCBuMmEHgD1HCfAEkGF0
|
||||
rwWxBaACMAnwdCXATSEWBCBXKAEgMTUgKG8kgB7gBJAJgCAHgA9QdRxtKS5uHiAw8XN0eYxsZTU/
|
||||
MPEhLS0Ko5AvKiBGAiEgRAEQMQuAaXRpAiAEICovpQqjQAISLWYA0GUKpBsBkQMwezpFIQBseTp0
|
||||
IkMxgGIHIQXQMhBoLiIagDrnCrBuIUBlLdwxOhWQHjAz8DM+oR4w7jY+wT5xGoB9Oe86/zwD+Txg
|
||||
bGk8kT0/PkMz4TPw3xWQRGIeMD9bOIFTNoI4/9EKwXAuTSFQTgWwAMC4bCwgQmBHig9QdkeID0DK
|
||||
AMAtYAuAOjBjbRdCuUAzAJB6JLAxMS4eMAUwS1476EJjIixz7wBxTEAGckK5bSFQQHEZcMcmcCGA
|
||||
C2BuZ3UfACSwoEVOLVVTP6ZzPeE6LkXiRSDwC3AZAHZawHBydnkxN0n7UHH1NnMtNoBwJLBVgBGg
|
||||
AiD7B0AhknA+EU0PTh9PL0ExRxjjJaALgGRvdzKQeEdM8T/ER4JDaHBGQWF+dR7gVA9VGFrgVnAA
|
||||
IC3/AiA8IFa/V89Y31A/UU8/8u8KsFGAM4MGYGM5YhxTQQaLTFMw4DJMwiA3OWajg2GKSsQ3MC44
|
||||
NWbSf2ivaTM/pkkiZL9BFWRiOv9rSj+mOBA2zx8xKUA2jzeYMlsGkCBnMpBiIiA5KF0+PB+xPgqj
|
||||
PG+/JhARgFWAAQFcMgQgdl4RezLBNMF0IhBSgA9AAMB4oSXAMTAyNiIQL3K1Ni9ygjfwWwnwD1Bm
|
||||
Xd9uX3EPch9zJQtgeQhgBUBXdCpyt3UScHQcZDIQYX91YXXJent5sHZvLow2Mi+fL68fAA5AH0AG
|
||||
4GR5SDCZYwE9QwXwQmBuayXAQCMwNTYzQ34xduGEZTk1NEYBwHTRb9LkPScn8i13MgApEDyQ7YIw
|
||||
a4cAKAEnLnAeowAhfwMwYvJ1gR8wiDYXoB5bOb0/MDxJITJQC2AEED1rSu+IGQAAiX8e0zYvcXzg
|
||||
i1QnR5eMf42DRG88kFwndwVwSRAJ8Cx3jS9xcyBw/XduNRWQftKTYYg2CqKIJ/8KcohHCrGWeInt
|
||||
AcBvkZNuX46vj7+Xn5KvHnk4HjAmnG5iUoACgIg4J2EBQP+cb5QflS+WP5dPmF+Zb5p/f5uPpXRW
|
||||
cZGQCYAZUKrgMRBtIHprDHAnOWFsZWILkKrhID4wUzI6/5z/ng+h/6MPpB+lL6Y/p0//qF+pb6Ef
|
||||
rc+e75//t3+vL3+wP7FPu3+zb7R/tY+2nyB6UJGROKrTAKCrwbsROY960KuiWqCroWU4a5GT/8di
|
||||
G1CRoBWQkZFzYKrRi1BXuxEBEKsiYnrQc8dhZfuJQZGgM4Owt7+4z7y/vc//vt+/78D/wg/DH8Qv
|
||||
u9/Lf/+5r7q/1S/M383vzv/ZL9Efn9Iv0z/UTwYAqpF6ZDIA/nYmUNVv1n/ab9t/3I/dn//er9+/
|
||||
4M/h39mP5K/XX9hv/+5f5g/nH+gv8l/qT+tf7G//7X8eTPDRg4D66ztR+78e1L40EgA2YD3hhkZM
|
||||
CTRMw41aFCOJMC9gN0Q7Yj+vY0KEMIgK+5lNCcAuPNC7U1HGE0IKwFxgqzFrHlz/hOBvkj3hiBn1
|
||||
KIn989L9WvcJP/9fAG83AZ8CrwO/+2//7s/lD/PIBx8ILwvJ9G/1f//2j/efL+4rQG+RSSEXfxuf
|
||||
34NADWEK8IOhd243gfIuEQUucH0jAAAAAwAWEAAAAAADAA00/T+lDgMADzT9P6UOAgEUNAEAAAAQ
|
||||
AAAATklUQfm/uAEAqgA32W4AAAIBfwABAAAAMQAAADAwMDAwMDAwNDVFQTg0QUIyNUExNDk0QjhD
|
||||
RDYxOEZDMjdFMDk0MEUwNENBNzUwMAAAAAADAAYQuPK1DAMABxBYAAAAAwAQEAAAAAADABEQAAAA
|
||||
AB4ACBABAAAAWQAAAERPQlL9REVOLFBPU+1M4U1aS1W5RUJO7UUtTUFJTDpQ+O1MSbm+TFW7T1Xo
|
||||
S/1L+fL6UOxM7+FCRUxTS+nzRFlTUE9aRFJBVkVNTUdSTUlMT7lCQVJU4UsAAAAAvTM=
|
||||
|
||||
------=_NextPart_000_001E_01D81E78.84E7EB50--
|
||||
|
|
@ -209,6 +209,7 @@ class BodyStructure implements \JsonSerializable
|
|||
/**
|
||||
* No text found, is it encrypted?
|
||||
* If so, just return that.
|
||||
* Only when \RainLoop\Api::Config()->Get('security', 'openpgp', true)
|
||||
*/
|
||||
if (!$aParts) {
|
||||
$gEncryptedParts = $this->SearchByContentType('multipart/encrypted');
|
||||
|
|
|
@ -148,18 +148,6 @@ class MailClient
|
|||
}
|
||||
$aFetchItems[] = $sLine;
|
||||
}
|
||||
/*
|
||||
$gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed');
|
||||
foreach ($gSignatureParts as $oPart) {
|
||||
if ($oPart->isPgpSigned()) {
|
||||
// An empty section specification refers to the entire message, including the header.
|
||||
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||
$aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().'.MIME]';
|
||||
$aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().']';
|
||||
$aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[1]->PartID().']';
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,11 +488,11 @@ class MailClient
|
|||
$aFetchIterator = $this->oImapClient->FetchIterate($aFetchItems, (string) $oRange, $oRange->UID);
|
||||
// FETCH does not respond in the id order of the SequenceSet, so we prefill $aCollection for the right sort order.
|
||||
$aCollection = \array_fill_keys($oRange->getArrayCopy(), null);
|
||||
foreach ($aFetchIterator as $oFetchResponseItem) {
|
||||
foreach ($aFetchIterator as $oFetchResponse) {
|
||||
$id = $oRange->UID
|
||||
? $oFetchResponseItem->GetFetchValue(FetchType::UID)
|
||||
: $oFetchResponseItem->oImapResponse->ResponseList[1];
|
||||
$oMessage = Message::fromFetchResponse($oMessageCollection->FolderName, $oFetchResponseItem);
|
||||
? $oFetchResponse->GetFetchValue(FetchType::UID)
|
||||
: $oFetchResponse->oImapResponse->ResponseList[1];
|
||||
$oMessage = Message::fromFetchResponse($oMessageCollection->FolderName, $oFetchResponse);
|
||||
if ($oMessage) {
|
||||
if ($aAllThreads) {
|
||||
$iUid = $oMessage->Uid;
|
||||
|
|
|
@ -112,6 +112,16 @@ class Message implements \JsonSerializable
|
|||
return $this->Attachments;
|
||||
}
|
||||
|
||||
public function setPlain(string $value) : void
|
||||
{
|
||||
$this->sPlain = $value;
|
||||
}
|
||||
|
||||
public function setHtml(string $value) : void
|
||||
{
|
||||
$this->sHtml = $value;
|
||||
}
|
||||
|
||||
private function setSpamScore($value) : void
|
||||
{
|
||||
$this->SpamScore = \intval(\max(0, \min(100, $value)));
|
||||
|
|
|
@ -515,6 +515,29 @@ trait Messages
|
|||
} catch (\Throwable $e) {
|
||||
$this->logException($e);
|
||||
}
|
||||
/*
|
||||
if (!$oMessage->sPlain && !$oMessage->sHtml && !$oMessage->pgpEncrypted && !$oMessage->smimeEncrypted) {
|
||||
$aAttachments = $oMessage->Attachments ?: [];
|
||||
foreach ($aAttachments as $oAttachment) {
|
||||
// \in_array($oAttachment->ContentType(), ['application/vnd.ms-tnef', 'application/ms-tnef'])
|
||||
if ('winmail.dat' === \strtolower($oAttachment->FileName())) {
|
||||
$sData = $this->ImapClient()->FetchMessagePart(
|
||||
$oMessage->Uid,
|
||||
$oAttachment->PartID()
|
||||
);
|
||||
$oTNEF = new \TNEFDecoder\TNEFAttachment;
|
||||
$oTNEF->decodeTnef($sData);
|
||||
foreach ($oTNEF->getFiles() as $oFile) {
|
||||
if (\in_array($oFile->type, ['application/rtf', 'text/rtf'])) {
|
||||
$rtf = new \SnappyMail\Rtf\Document($oFile->content);
|
||||
$oMessage->setHtml($rtf->toHTML());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
catch (\Throwable $oException)
|
||||
{
|
||||
|
|
339
snappymail/v/0.0.0/app/libraries/TNEFDecoder/LICENSE
Normal file
339
snappymail/v/0.0.0/app/libraries/TNEFDecoder/LICENSE
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 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.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
372
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFAttachment.php
Normal file
372
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFAttachment.php
Normal file
|
@ -0,0 +1,372 @@
|
|||
<?php
|
||||
|
||||
namespace TNEFDecoder;
|
||||
|
||||
require 'functions.php';
|
||||
require 'constants.php';
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
class TNEFAttachment
|
||||
{
|
||||
|
||||
var $validateChecksum;
|
||||
var $mailinfo;
|
||||
var $files;
|
||||
var $files_nested;
|
||||
var $attachments;
|
||||
var $current_receiver;
|
||||
var $body;
|
||||
|
||||
function __construct($validateChecksum = false)
|
||||
{
|
||||
$this->validateChecksum = $validateChecksum;
|
||||
$this->files = array();
|
||||
$this->attachments = array();
|
||||
$this->mailinfo = new TNEFMailinfo();
|
||||
$this->body = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TNEFFileBase[]
|
||||
*/
|
||||
function &getFiles()
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TNEFFileBase[]
|
||||
*/
|
||||
function &getFilesNested()
|
||||
{
|
||||
if (!$this->files_nested)
|
||||
{
|
||||
$this->files_nested = array();
|
||||
|
||||
$num_attach = count($this->attachments);
|
||||
if ($num_attach > 0)
|
||||
{
|
||||
for ($cnt = 0; $cnt < $num_attach; $cnt++)
|
||||
{
|
||||
$this->addFiles($this->files_nested, $this->files);
|
||||
$this->addFiles($this->files_nested, $this->attachments[$cnt]->getFilesNested());
|
||||
}
|
||||
}
|
||||
else
|
||||
$this->addFiles($this->files_nested, $this->files);
|
||||
}
|
||||
|
||||
return $this->files_nested;
|
||||
}
|
||||
|
||||
function addFiles(&$add_to, &$add)
|
||||
{
|
||||
global $tnef_minimum_rtf_size_to_decode;
|
||||
$num_files = count($add);
|
||||
for ($cnt = 0; $cnt < $num_files; $cnt++)
|
||||
if ((strtolower(get_class($add[$cnt])) != "tneffilertf") || ($add[$cnt]->getSize() > $tnef_minimum_rtf_size_to_decode))
|
||||
$add_to[] = &$add[$cnt];
|
||||
}
|
||||
|
||||
function addFilesCond(&$add_to, &$add)
|
||||
{
|
||||
global $tnef_minimum_rtf_size_to_decode;
|
||||
$num_files = count($add);
|
||||
for ($cnt = 0; $cnt < $num_files; $cnt++)
|
||||
if ((strtolower(get_class($add[$cnt])) == "tneffilertf") && ($add[$cnt]->getSize() > $tnef_minimum_rtf_size_to_decode))
|
||||
$add_to[] = &$add[$cnt];
|
||||
}
|
||||
|
||||
function getAttachments()
|
||||
{
|
||||
return $this->attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TNEFMailinfo
|
||||
*/
|
||||
function getMailinfo()
|
||||
{
|
||||
return $this->mailinfo;
|
||||
}
|
||||
|
||||
function getBodyElements()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
function decodeTnef($data)
|
||||
{
|
||||
$buffer = new TNEFBuffer($data);
|
||||
|
||||
$tnef_signature = tnef_geti32($buffer);
|
||||
if ($tnef_signature == TNEF_SIGNATURE) {
|
||||
$tnef_key = tnef_geti16($buffer);
|
||||
tnef_log(sprintf("Signature: 0x%08x\nKey: 0x%04x\n", $tnef_signature, $tnef_key));
|
||||
|
||||
while ($buffer->getRemainingBytes() > 0) {
|
||||
$lvl_type = tnef_geti8($buffer);
|
||||
|
||||
switch ($lvl_type) {
|
||||
case TNEF_LVL_MESSAGE:
|
||||
$this->tnef_decode_attribute($buffer);
|
||||
break;
|
||||
|
||||
case TNEF_LVL_ATTACHMENT:
|
||||
$this->tnef_decode_attribute($buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
$len = $buffer->getRemainingBytes();
|
||||
if ($len > 0)
|
||||
tnef_log("Invalid file format! Unknown Level $lvl_type. Rest=$len");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tnef_log("Invalid file format! Wrong signature.");
|
||||
}
|
||||
|
||||
// propagate parent message's code page to child files if given
|
||||
//
|
||||
$code_page = $this->mailinfo->getCodePage();
|
||||
if (!empty($code_page))
|
||||
foreach ($this->files as $i => $file)
|
||||
$this->files[$i]->setMessageCodePage($code_page);
|
||||
}
|
||||
|
||||
function tnef_decode_attribute(TNEFBuffer $buffer)
|
||||
{
|
||||
$attribute = tnef_geti32($buffer); // attribute if
|
||||
$length = tnef_geti32($buffer); // length
|
||||
$value = tnef_getx($length, $buffer); // data
|
||||
$checksumAtt = tnef_geti16($buffer); // checksum
|
||||
if ($value !== null && $this->validateChecksum) {
|
||||
$checksum = array_sum(unpack('C*', $value)) & 0xFFFF;
|
||||
if ($checksum !== $checksumAtt) {
|
||||
throw new \Exception('Checksums do not match');
|
||||
}
|
||||
}
|
||||
|
||||
switch($attribute)
|
||||
{
|
||||
case TNEF_ARENDDATA: // marks start of new attachment
|
||||
tnef_log("Creating new File for Attachment");
|
||||
$this->current_receiver = new TNEFFile();
|
||||
$this->files[] = $this->current_receiver;
|
||||
break;
|
||||
|
||||
case TNEF_AMAPIATTRS:
|
||||
tnef_log("mapi attrs");
|
||||
$this->extract_mapi_attrs(new TNEFBuffer($value));
|
||||
break;
|
||||
|
||||
case TNEF_AMAPIPROPS:
|
||||
tnef_log("mapi props");
|
||||
$this->extract_mapi_attrs(new TNEFBuffer($value));
|
||||
break;
|
||||
|
||||
case TNEF_AMCLASS:
|
||||
$value = substr($value, 0, $length - 1);
|
||||
if ($value == 'IPM.Contact')
|
||||
{
|
||||
tnef_log("Creating vCard Attachment");
|
||||
$this->current_receiver = new TNEFvCard();
|
||||
$this->files[] = $this->current_receiver;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->mailinfo->receiveTnefAttribute($attribute, $value, $length);
|
||||
if ($this->current_receiver)
|
||||
$this->current_receiver->receiveTnefAttribute($attribute, $value, $length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function extract_mapi_attrs(TNEFBuffer $buffer)
|
||||
{
|
||||
|
||||
$number = tnef_geti32($buffer); // number of attributes
|
||||
$props = 0;
|
||||
$ended = 0;
|
||||
|
||||
while (($buffer->getRemainingBytes() > 0) && ($props < $number) && (!$ended))
|
||||
{
|
||||
$props++;
|
||||
$value = '';
|
||||
unset($named_id);
|
||||
$length = 0;
|
||||
$have_multivalue = 0;
|
||||
$num_multivalues = 1;
|
||||
$attr_type = tnef_geti16($buffer);
|
||||
$attr_name = tnef_geti16($buffer);
|
||||
|
||||
if (($attr_type & TNEF_MAPI_MV_FLAG) != 0)
|
||||
{
|
||||
tnef_log("Multivalue Attribute found.");
|
||||
$have_multivalue = 1;
|
||||
$attr_type = $attr_type & ~TNEF_MAPI_MV_FLAG;
|
||||
}
|
||||
|
||||
if (($attr_name >= 0x8000) && ($attr_name < 0xFFFE)) // Named Attribute
|
||||
{
|
||||
$guid = tnef_getx(16, $buffer);
|
||||
$named_type = tnef_geti32($buffer);
|
||||
switch ($named_type)
|
||||
{
|
||||
case TNEF_MAPI_NAMED_TYPE_ID:
|
||||
$named_id = tnef_geti32($buffer);
|
||||
$attr_name = $named_id;
|
||||
tnef_log(sprintf("Named Id='0x%04x'", $named_id));
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_NAMED_TYPE_STRING:
|
||||
$attr_name = 0x9999; // dummy to identify strings
|
||||
$idlen = tnef_geti32($buffer);
|
||||
tnef_log("idlen=$idlen");
|
||||
$buflen = $idlen + ((4 - ($idlen % 4)) % 4); // pad to next 4 byte boundary
|
||||
tnef_log("buflen=$buflen");
|
||||
$named_id = substr(tnef_getx($buflen, $buffer), 0, $idlen ); // read and truncate to length
|
||||
tnef_log("Named Id='$named_id'");
|
||||
break;
|
||||
|
||||
default:
|
||||
tnef_log(sprintf("Unknown Named Type 0x%04x found", $named_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($have_multivalue)
|
||||
{
|
||||
$num_multivalues = tnef_geti32($buffer);
|
||||
tnef_log("Number of multivalues=$num_multivalues");
|
||||
}
|
||||
|
||||
switch($attr_type)
|
||||
{
|
||||
case TNEF_MAPI_NULL:
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_SHORT:
|
||||
$value = tnef_geti16($buffer);
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_INT:
|
||||
case TNEF_MAPI_BOOLEAN:
|
||||
for ($cnt = 0; $cnt < $num_multivalues; $cnt++)
|
||||
$value = tnef_geti32($buffer);
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_FLOAT:
|
||||
case TNEF_MAPI_ERROR:
|
||||
$value = tnef_getx(4, $buffer);
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_DOUBLE:
|
||||
case TNEF_MAPI_APPTIME:
|
||||
case TNEF_MAPI_CURRENCY:
|
||||
case TNEF_MAPI_INT8BYTE:
|
||||
case TNEF_MAPI_SYSTIME:
|
||||
$value = tnef_getx(8, $buffer);
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_CLSID:
|
||||
tnef_log("What is a MAPI CLSID ????");
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_STRING:
|
||||
case TNEF_MAPI_UNICODE_STRING:
|
||||
case TNEF_MAPI_BINARY:
|
||||
case TNEF_MAPI_OBJECT:
|
||||
if ($have_multivalue)
|
||||
$num_vals = $num_multivalues;
|
||||
else
|
||||
$num_vals = tnef_geti32($buffer);
|
||||
|
||||
if ($num_vals > 20) // A Sanity check.
|
||||
{
|
||||
$ended = 1;
|
||||
tnef_log("Number of entries in String Attributes=$num_vals. Aborting Mapi parsing.");
|
||||
}
|
||||
else
|
||||
{
|
||||
for ($cnt = 0; $cnt < $num_vals; $cnt++)
|
||||
{
|
||||
$length = tnef_geti32($buffer);
|
||||
$buflen = $length + ((4 - ($length % 4)) % 4); // pad to next 4 byte boundary
|
||||
if ($attr_type == TNEF_MAPI_STRING)
|
||||
$length -= 1;
|
||||
$value = substr(tnef_getx($buflen, $buffer), 0, $length); // read and truncate to length
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
tnef_log("Unknown mapi attribute! $attr_type");
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($attr_name)
|
||||
{
|
||||
case TNEF_MAPI_ATTACH_DATA:
|
||||
tnef_log("MAPI Found nested attachment. Processing new one.");
|
||||
$value = substr($value, 16); // skip the next 16 bytes (unknown data)
|
||||
$att = new TNEFAttachment($this->validateChecksum);
|
||||
$att->decodeTnef($value);
|
||||
$this->attachments[] = $att;
|
||||
tnef_log("MAPI Finished nested attachment. Continuing old one.");
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_RTF_COMPRESSED:
|
||||
tnef_log("MAPI Found Compressed RTF Attachment.");
|
||||
$this->files[] = new TNEFFileRTF($value);
|
||||
break;
|
||||
case TNEF_MAPI_BODY:
|
||||
case TNEF_MAPI_BODY_HTML:
|
||||
$result = [];
|
||||
$result['type'] = 'text';
|
||||
$result['subtype'] = $attr_name == TNEF_MAPI_BODY ? 'plain' : 'html';
|
||||
$result['name'] = ('Untitled') . ($attr_name == TNEF_MAPI_BODY ? '.txt' : '.html');
|
||||
$result['stream'] = $value;
|
||||
$result['size'] = strlen($value);
|
||||
$this->body[] = $result;
|
||||
break;
|
||||
default:
|
||||
$this->mailinfo->receiveMapiAttribute($attr_type, $attr_name, $value, $length, ($attr_type == TNEF_MAPI_UNICODE_STRING));
|
||||
if ($this->current_receiver)
|
||||
$this->current_receiver->receiveMapiAttribute($attr_type, $attr_name, $value, $length, ($attr_type == TNEF_MAPI_UNICODE_STRING));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ended)
|
||||
{
|
||||
$len = $buffer->getRemainingBytes();
|
||||
for ($cnt = 0; $cnt < $len; $cnt++)
|
||||
{
|
||||
$ord = tnef_geti8($buffer);
|
||||
if ($ord == 0)
|
||||
$char = "";
|
||||
else
|
||||
$char = chr($ord);
|
||||
tnef_log(sprintf("Char Nr. %6d = 0x%02x = '%s'", $cnt, $ord, $char));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFBuffer.php
Normal file
48
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFBuffer.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFBuffer
|
||||
{
|
||||
private string $data;
|
||||
private int $offset;
|
||||
|
||||
function __construct(string $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->offset = 0;
|
||||
}
|
||||
|
||||
function getBytes(int $numBytes): ?string
|
||||
{
|
||||
if ($this->getRemainingBytes() < $numBytes) {
|
||||
$this->offset = \strlen($this->data);
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->offset += $numBytes;
|
||||
return \substr($this->data, $this->offset - $numBytes, $numBytes);
|
||||
}
|
||||
|
||||
function getRemainingBytes(): int
|
||||
{
|
||||
return \strlen($this->data) - $this->offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
44
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFDate.php
Normal file
44
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFDate.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFDate
|
||||
{
|
||||
|
||||
private ?int $year;
|
||||
private ?int $month;
|
||||
private ?int $day;
|
||||
private ?int $hour;
|
||||
private ?int $minute;
|
||||
private ?int $second;
|
||||
|
||||
public function setTnefBuffer(TNEFBuffer $buffer)
|
||||
{
|
||||
$this->year = tnef_geti16($buffer);
|
||||
$this->month = tnef_geti16($buffer);
|
||||
$this->day = tnef_geti16($buffer);
|
||||
$this->hour = tnef_geti16($buffer);
|
||||
$this->minute = tnef_geti16($buffer);
|
||||
$this->second = tnef_geti16($buffer);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return \sprintf("%04d-%02d-%02d %02d:%02d:%02d",
|
||||
$this->year, $this->month, $this->day,
|
||||
$this->hour, $this->minute, $this->second);
|
||||
}
|
||||
|
||||
}
|
118
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFile.php
Normal file
118
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFile.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFFile extends TNEFFileBase
|
||||
{
|
||||
|
||||
var $metafile;
|
||||
|
||||
function getMetafile()
|
||||
{
|
||||
return $this->metafile;
|
||||
}
|
||||
|
||||
function receiveTnefAttribute($attribute, $value, $length)
|
||||
{
|
||||
switch ($attribute)
|
||||
{
|
||||
|
||||
// filename
|
||||
//
|
||||
case TNEF_AFILENAME:
|
||||
// strip path
|
||||
//
|
||||
if (($pos = strrpos($value, '/')) !== FALSE)
|
||||
$this->name = substr($value, $pos + 1);
|
||||
else
|
||||
$this->name = $value;
|
||||
|
||||
// Strip trailing null bytes if present
|
||||
$this->name = trim($this->name);
|
||||
break;
|
||||
// code page
|
||||
//
|
||||
case TNEF_AOEMCODEPAGE:
|
||||
$this->code_page = tnef_geti16(new TNEFBuffer($value));
|
||||
break;
|
||||
|
||||
// the attachment itself
|
||||
//
|
||||
case TNEF_ATTACHDATA:
|
||||
$this->content = $value;
|
||||
break;
|
||||
|
||||
// a metafile
|
||||
//
|
||||
case TNEF_ATTACHMETAFILE:
|
||||
$this->metafile = $value;
|
||||
break;
|
||||
|
||||
case TNEF_AATTACHCREATEDATE:
|
||||
$this->created = new TNEFDate();
|
||||
$this->created->setTnefBuffer(new TNEFBuffer($value));
|
||||
|
||||
case TNEF_AATTACHMODDATE:
|
||||
$this->modified = new TNEFDate();
|
||||
$this->modified->setTnefBuffer(new TNEFBuffer($value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unicode=FALSE)
|
||||
{
|
||||
switch ($attr_name)
|
||||
{
|
||||
|
||||
// used in preference to AFILENAME value
|
||||
//
|
||||
case TNEF_MAPI_ATTACH_LONG_FILENAME:
|
||||
// strip path
|
||||
//
|
||||
if (($pos = strrpos($value, '/')) !== FALSE)
|
||||
$this->name = substr($value, $pos + 1);
|
||||
else
|
||||
$this->name = $value;
|
||||
|
||||
if ($is_unicode) $this->name_is_unicode = TRUE;
|
||||
break;
|
||||
|
||||
// Is this ever set, and what is format?
|
||||
//
|
||||
case TNEF_MAPI_ATTACH_MIME_TAG:
|
||||
$type0 = $type1 = '';
|
||||
$mime_type = explode('/', $value, 2);
|
||||
if (!empty($mime_type[0]))
|
||||
$type0 = $mime_type[0];
|
||||
if (!empty($mime_type[1]))
|
||||
$type1 = $mime_type[1];
|
||||
$this->type = "$type0/$type1";
|
||||
if ($is_unicode) {
|
||||
$this->type = substr(mb_convert_encoding($this->type, "UTF-8" , "UTF-16LE"), 0, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_ATTACH_EXTENSION:
|
||||
$type = \SnappyMail\File\MimeType::fromFilename($value);
|
||||
if ($type)
|
||||
$this->type = $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFFileBase
|
||||
{
|
||||
public bool $name_is_unicode = FALSE;
|
||||
public string $name = 'Untitled';
|
||||
public string $code_page = '';
|
||||
public string $message_code_page = ''; // parent message's code page (the whole TNEF file)
|
||||
public string $type = 'application/octet-stream';
|
||||
public string $content = '';
|
||||
var $created;
|
||||
var $modified;
|
||||
|
||||
function setMessageCodePage(string $code_page)
|
||||
{
|
||||
$this->message_code_page = $code_page;
|
||||
}
|
||||
|
||||
function getCodePage()
|
||||
{
|
||||
return empty($this->code_page)
|
||||
? $this->message_code_page
|
||||
: $this->code_page;
|
||||
}
|
||||
|
||||
function getName()
|
||||
{
|
||||
if ($this->name_is_unicode) {
|
||||
return \substr(\mb_convert_encoding($this->name, "UTF-8" , "UTF-16LE"), 0, -1);
|
||||
}
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
function getSize()
|
||||
{
|
||||
return \strlen($this->content);
|
||||
}
|
||||
}
|
111
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileRTF.php
Normal file
111
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileRTF.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
class TNEFFileRTF extends TNEFFileBase
|
||||
{
|
||||
var $size;
|
||||
const MAX_DICT_SIZE = 4096;
|
||||
const INIT_DICT_SIZE = 207;
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->type = "application/rtf";
|
||||
$this->name = "EmbeddedRTF.rtf";
|
||||
|
||||
$this->decode_crtf(new TNEFBuffer($data));
|
||||
}
|
||||
|
||||
function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
function decode_crtf(TNEFBuffer $buffer)
|
||||
{
|
||||
$size_compressed = tnef_geti32($buffer);
|
||||
$this->size = tnef_geti32($buffer);
|
||||
$magic = tnef_geti32($buffer);
|
||||
$crc32 = tnef_geti32($buffer);
|
||||
|
||||
tnef_log("CRTF: size comp=$size_compressed, size=$this->size");
|
||||
|
||||
$data = tnef_getx($buffer->getRemainingBytes(), $buffer);
|
||||
|
||||
switch ($magic) {
|
||||
case CRTF_COMPRESSED:
|
||||
$this->uncompress($data);
|
||||
break;
|
||||
|
||||
case CRTF_UNCOMPRESSED:
|
||||
$this->content = $data;
|
||||
break;
|
||||
|
||||
default:
|
||||
tnef_log("Unknown Compressed RTF Format");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function uncompress($data)
|
||||
{
|
||||
$preload = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
|
||||
$length_preload = strlen($preload);
|
||||
$init_dict = [];
|
||||
for ($cnt = 0; $cnt < $length_preload; $cnt++) {
|
||||
$init_dict[$cnt] = $preload[$cnt];
|
||||
}
|
||||
$init_dict = array_merge($init_dict, array_fill(count($init_dict), self::MAX_DICT_SIZE - $length_preload, ' '));
|
||||
$write_offset = self::INIT_DICT_SIZE;
|
||||
$this->content = '';
|
||||
$end = false;
|
||||
$in = 0;
|
||||
$l = strlen($data);
|
||||
while (!$end) {
|
||||
if ($in >= $l) {
|
||||
break;
|
||||
}
|
||||
$control = strrev(str_pad(decbin(ord($data[$in++])), 8, 0, STR_PAD_LEFT));
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
if ($control[$i] == '1') {
|
||||
$token = unpack("n", $data[$in++] . $data[$in++])[1];
|
||||
$offset = ($token >> 4) & 0b111111111111;
|
||||
$length = $token & 0b1111;
|
||||
if ($write_offset == $offset) {
|
||||
$end = true;
|
||||
break;
|
||||
}
|
||||
$actual_length = $length + 2;
|
||||
for ($step = 0; $step < $actual_length; $step++) {
|
||||
$read_offset = ($offset + $step) % self::MAX_DICT_SIZE;
|
||||
$char = $init_dict[$read_offset];
|
||||
$this->content .= $char;
|
||||
$init_dict[$write_offset] = $char;
|
||||
$write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE;
|
||||
}
|
||||
} else {
|
||||
if ($in >= $l) {
|
||||
break;
|
||||
}
|
||||
$val = $data[$in++];
|
||||
$this->content .= $val;
|
||||
$init_dict[$write_offset] = $val;
|
||||
$write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
109
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFMailinfo.php
Normal file
109
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFMailinfo.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFMailinfo
|
||||
{
|
||||
|
||||
var $subject;
|
||||
var $topic;
|
||||
var $topic_is_unicode = FALSE;
|
||||
var $from;
|
||||
var $from_is_unicode = FALSE;
|
||||
var $from_name;
|
||||
var $from_name_is_unicode = FALSE;
|
||||
var $date_sent;
|
||||
var $code_page = '';
|
||||
|
||||
|
||||
function getTopic()
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
function getFrom()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
function getCodePage()
|
||||
{
|
||||
return $this->code_page;
|
||||
}
|
||||
|
||||
function getFromName()
|
||||
{
|
||||
return $this->from_name;
|
||||
}
|
||||
|
||||
function getDateSent()
|
||||
{
|
||||
return $this->date_sent;
|
||||
}
|
||||
|
||||
function receiveTnefAttribute($attribute, $value, $length)
|
||||
{
|
||||
$value = new TNEFBuffer($value);
|
||||
|
||||
switch($attribute)
|
||||
{
|
||||
case TNEF_AOEMCODEPAGE:
|
||||
$this->code_page = tnef_geti16($value);
|
||||
break;
|
||||
|
||||
case TNEF_ASUBJECT:
|
||||
$this->subject = tnef_getx($length - 1, $value);
|
||||
break;
|
||||
|
||||
case TNEF_ADATERECEIVED:
|
||||
if (!$this->date_sent)
|
||||
{
|
||||
$this->date_sent = new TNEFDate();
|
||||
$this->date_sent->setTnefBuffer($value);
|
||||
}
|
||||
break;
|
||||
case TNEF_ADATESENT:
|
||||
$this->date_sent = new TNEFDate();
|
||||
$this->date_sent->setTnefBuffer($value);
|
||||
}
|
||||
}
|
||||
|
||||
function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unicode=FALSE)
|
||||
{
|
||||
switch($attr_name)
|
||||
{
|
||||
case TNEF_MAPI_CONVERSATION_TOPIC:
|
||||
$this->topic = $value;
|
||||
if ($is_unicode) $this->topic_is_unicode = TRUE;
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_SENT_REP_EMAIL_ADDR:
|
||||
$this->from = $value;
|
||||
if ($is_unicode) $this->from_is_unicode = TRUE;
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_SENT_REP_NAME:
|
||||
$this->from_name = $value;
|
||||
if ($is_unicode) $this->from_name_is_unicode = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
302
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFvCard.php
Normal file
302
snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFvCard.php
Normal file
|
@ -0,0 +1,302 @@
|
|||
<?php
|
||||
|
||||
namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
class TNEFvCard extends TNEFFileBase
|
||||
{
|
||||
|
||||
public bool $name_is_unicode = FALSE;
|
||||
public string $name = 'Untitled';
|
||||
public string $code_page = '';
|
||||
public string $message_code_page = ''; // parent message's code page (the whole TNEF file)
|
||||
public string $type = 'text/x-vcard';
|
||||
public string $content = '';
|
||||
var $metafile;
|
||||
var $surname;
|
||||
var $surname_is_unicode = FALSE;
|
||||
var $given_name;
|
||||
var $given_name_is_unicode = FALSE;
|
||||
var $middle_name;
|
||||
var $middle_name_is_unicode = FALSE;
|
||||
var $nickname;
|
||||
var $nickname_is_unicode = FALSE;
|
||||
var $company;
|
||||
var $company_is_unicode = FALSE;
|
||||
var $homepages;
|
||||
var $addresses;
|
||||
var $emails;
|
||||
var $telefones;
|
||||
|
||||
private static
|
||||
$address_mapping = array (
|
||||
TNEF_MAPI_LOCALTY => array ("Address", ADDRESS_CITY),
|
||||
TNEF_MAPI_COUNTRY => array ("Address", ADDRESS_COUNTRY),
|
||||
TNEF_MAPI_POSTAL_CODE => array ("Address", ADDRESS_ZIP),
|
||||
TNEF_MAPI_STATE_OR_PROVINCE => array ("Address", ADDRESS_STATE),
|
||||
TNEF_MAPI_STREET_ADDRESS => array ("Address", ADDRESS_STREET),
|
||||
TNEF_MAPI_POST_OFFICE_BOX => array ("Address", ADDRESS_PO_BOX),
|
||||
TNEF_MAPI_HOME_ADDR_CITY => array ("Home Address", ADDRESS_CITY),
|
||||
TNEF_MAPI_HOME_ADDR_COUNTRY => array ("Home Address", ADDRESS_COUNTRY),
|
||||
TNEF_MAPI_HOME_ADDR_ZIP => array ("Home Address", ADDRESS_ZIP),
|
||||
TNEF_MAPI_HOME_ADDR_STATE => array ("Home Address", ADDRESS_STATE),
|
||||
TNEF_MAPI_HOME_ADDR_STREET => array ("Home Address", ADDRESS_STREET),
|
||||
TNEF_MAPI_HOME_ADDR_PO_BOX => array ("Home Address", ADDRESS_PO_BOX),
|
||||
TNEF_MAPI_OTHER_ADDR_CITY => array ("Other Address", ADDRESS_CITY),
|
||||
TNEF_MAPI_OTHER_ADDR_COUNTRY => array ("Other Address", ADDRESS_COUNTRY),
|
||||
TNEF_MAPI_OTHER_ADDR_ZIP => array ("Other Address", ADDRESS_ZIP),
|
||||
TNEF_MAPI_OTHER_ADDR_STATE => array ("Other Address", ADDRESS_STATE),
|
||||
TNEF_MAPI_OTHER_ADDR_STREET => array ("Other Address", ADDRESS_STREET),
|
||||
TNEF_MAPI_OTHER_ADDR_PO_BOX => array ("Other Address", ADDRESS_PO_BOX),
|
||||
),
|
||||
$email_mapping = array (
|
||||
TNEF_MAPI_EMAIL1_DISPLAY => array ("Email 1", EMAIL_DISPLAY),
|
||||
TNEF_MAPI_EMAIL1_TRANSPORT => array ("Email 1", EMAIL_TRANSPORT),
|
||||
TNEF_MAPI_EMAIL1_EMAIL => array ("Email 1", EMAIL_EMAIL),
|
||||
TNEF_MAPI_EMAIL1_EMAIL2 => array ("Email 1", EMAIL_EMAIL2),
|
||||
TNEF_MAPI_EMAIL2_DISPLAY => array ("Email 2", EMAIL_DISPLAY),
|
||||
TNEF_MAPI_EMAIL2_TRANSPORT => array ("Email 2", EMAIL_TRANSPORT),
|
||||
TNEF_MAPI_EMAIL2_EMAIL => array ("Email 2", EMAIL_EMAIL),
|
||||
TNEF_MAPI_EMAIL2_EMAIL2 => array ("Email 2", EMAIL_EMAIL2),
|
||||
TNEF_MAPI_EMAIL3_DISPLAY => array ("Email 3", EMAIL_DISPLAY),
|
||||
TNEF_MAPI_EMAIL3_TRANSPORT => array ("Email 3", EMAIL_TRANSPORT),
|
||||
TNEF_MAPI_EMAIL3_EMAIL => array ("Email 3", EMAIL_EMAIL),
|
||||
TNEF_MAPI_EMAIL3_EMAIL2 => array ("Email 3", EMAIL_EMAIL2),
|
||||
),
|
||||
$homepage_mapping = array (
|
||||
TNEF_MAPI_PERSONAL_HOME_PAGE => "Personal Homepage",
|
||||
TNEF_MAPI_BUSINESS_HOME_PAGE => "Business Homepage",
|
||||
TNEF_MAPI_OTHER_HOME_PAGE => "Other Homepage",
|
||||
),
|
||||
$telefone_mapping = array (
|
||||
TNEF_MAPI_PRIMARY_TEL_NUMBER => "Primary Telefone",
|
||||
TNEF_MAPI_HOME_TEL_NUMBER => "Home Telefone",
|
||||
TNEF_MAPI_HOME2_TEL_NUMBER => "Home2 Telefone",
|
||||
TNEF_MAPI_BUSINESS_TEL_NUMBER => "Business Telefone",
|
||||
TNEF_MAPI_BUSINESS2_TEL_NUMBER => "Business2 Telefone",
|
||||
TNEF_MAPI_MOBILE_TEL_NUMBER => "Mobile Telefone",
|
||||
TNEF_MAPI_RADIO_TEL_NUMBER => "Radio Telefone",
|
||||
TNEF_MAPI_CAR_TEL_NUMBER => "Car Telefone",
|
||||
TNEF_MAPI_OTHER_TEL_NUMBER => "Other Telefone",
|
||||
TNEF_MAPI_PAGER_TEL_NUMBER => "Pager Telefone",
|
||||
TNEF_MAPI_PRIMARY_FAX_NUMBER => "Primary Fax",
|
||||
TNEF_MAPI_BUSINESS_FAX_NUMBER => "Business Fax",
|
||||
TNEF_MAPI_HOME_FAX_NUMBER => "Home Fax",
|
||||
);
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->telefones = array();
|
||||
$this->homepages = array();
|
||||
$this->emails = array();
|
||||
$this->addresses = array();
|
||||
}
|
||||
|
||||
function getSurname()
|
||||
{
|
||||
return $this->surname;
|
||||
}
|
||||
|
||||
function getGivenName()
|
||||
{
|
||||
return $this->given_name;
|
||||
}
|
||||
|
||||
function getMiddleName()
|
||||
{
|
||||
return $this->middle_name;
|
||||
}
|
||||
|
||||
function getNickname()
|
||||
{
|
||||
return $this->nickname;
|
||||
}
|
||||
|
||||
function getCompany()
|
||||
{
|
||||
return $this->company;
|
||||
}
|
||||
|
||||
function getAddresses()
|
||||
{
|
||||
return $this->addresses;
|
||||
}
|
||||
|
||||
function getMetafile()
|
||||
{
|
||||
return $this->metafile;
|
||||
}
|
||||
|
||||
function getTelefones()
|
||||
{
|
||||
return $this->telefones;
|
||||
}
|
||||
|
||||
function getHomepages()
|
||||
{
|
||||
return $this->homepages;
|
||||
}
|
||||
|
||||
function getEmails()
|
||||
{
|
||||
return $this->emails;
|
||||
}
|
||||
|
||||
function receiveTnefAttribute($attribute, $value, $length)
|
||||
{
|
||||
switch ($attribute)
|
||||
{
|
||||
|
||||
// code page
|
||||
//
|
||||
case TNEF_AOEMCODEPAGE:
|
||||
$this->code_page = tnef_geti16(new TNEFBuffer($value));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unicode=FALSE)
|
||||
{
|
||||
switch($attr_name)
|
||||
{
|
||||
case TNEF_MAPI_DISPLAY_NAME:
|
||||
$this->name = $value;
|
||||
|
||||
if ($is_unicode) $this->name_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_SURNAME:
|
||||
$this->surname = $value;
|
||||
|
||||
if ($is_unicode) $this->surname_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_GIVEN_NAME:
|
||||
$this->given_name = $value;
|
||||
|
||||
if ($is_unicode) $this->given_name_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_MIDDLE_NAME:
|
||||
$this->middle_name = $value;
|
||||
|
||||
if ($is_unicode) $this->middle_name_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_NICKNAME:
|
||||
$this->nickname = $value;
|
||||
|
||||
if ($is_unicode) $this->nickname_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
case TNEF_MAPI_COMPANY_NAME:
|
||||
$this->company = $value;
|
||||
|
||||
if ($is_unicode) $this->company_is_unicode = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$rc = $this->evaluateTelefoneAttribute($attr_type, $attr_name, $value, $length);
|
||||
if (!$rc)
|
||||
$rc = $this->evaluateEmailAttribute($attr_type, $attr_name, $value, $length);
|
||||
if (!$rc)
|
||||
$rc = $this->evaluateAddressAttribute($attr_type, $attr_name, $value, $length);
|
||||
if (!$rc)
|
||||
$rc = $this->evaluateHomepageAttribute($attr_type, $attr_name, $value, $length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateTelefoneAttribute($attr_type, $attr_name, $value, $length)
|
||||
{
|
||||
$rc = 0;
|
||||
|
||||
if ($length > 0)
|
||||
{
|
||||
if (array_key_exists($attr_name, static::$telefone_mapping))
|
||||
{
|
||||
$telefone_key = static::$telefone_mapping[$attr_name];
|
||||
$this->telefones[$telefone_key] = $value;
|
||||
$rc = 1;
|
||||
tnef_log("Setting telefone '$telefone_key' to value '$value'");
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
function evaluateEmailAttribute($attr_type, $attr_name, $value, $length)
|
||||
{
|
||||
$rc = 0;
|
||||
|
||||
if ($length > 0)
|
||||
{
|
||||
if (array_key_exists($attr_name, static::$email_mapping))
|
||||
{
|
||||
$email_key = static::$email_mapping[$attr_name];
|
||||
if (!array_key_exists($email_key[0], $this->emails))
|
||||
$this->emails[$email_key[0]] = array ( EMAIL_DISPLAY => "", EMAIL_TRANSPORT => "", EMAIL_EMAIL => "", EMAIL_EMAIL2 => "");
|
||||
$this->emails[$email_key[0]][$email_key[1]] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
function evaluateAddressAttribute($attr_type, $attr_name, $value, $length)
|
||||
{
|
||||
$rc = 0;
|
||||
|
||||
if ($length > 0)
|
||||
{
|
||||
if (array_key_exists($attr_name, static::$address_mapping))
|
||||
{
|
||||
$address_key = static::$address_mapping[$attr_name];
|
||||
if (!array_key_exists($address_key[0], $this->addresses))
|
||||
$this->addresses[$address_key[0]] = array ( );
|
||||
$this->addresses[$address_key[0]][$address_key[1]] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
function evaluateHomepageAttribute($attr_type, $attr_name, $value, $length)
|
||||
{
|
||||
$rc = 0;
|
||||
|
||||
if ($length > 0)
|
||||
{
|
||||
if (array_key_exists($attr_name, static::$homepage_mapping))
|
||||
{
|
||||
$homepage_key = static::$homepage_mapping[$attr_name];
|
||||
$this->homepages[$homepage_key] = $value;
|
||||
$rc = 1;
|
||||
tnef_log("Setting homepage '$homepage_key' to value '$value'");
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
}
|
203
snappymail/v/0.0.0/app/libraries/TNEFDecoder/constants.php
Normal file
203
snappymail/v/0.0.0/app/libraries/TNEFDecoder/constants.php
Normal file
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
define("TNEF_SIGNATURE", 0x223e9f78);
|
||||
define("TNEF_LVL_MESSAGE", 0x01);
|
||||
define("TNEF_LVL_ATTACHMENT", 0x02);
|
||||
|
||||
define("TNEF_TRIPLES", 0x00000000);
|
||||
define("TNEF_STRING", 0x00010000);
|
||||
define("TNEF_TEXT", 0x00020000);
|
||||
define("TNEF_DATE", 0x00030000);
|
||||
define("TNEF_SHORT", 0x00040000);
|
||||
define("TNEF_LONG", 0x00050000);
|
||||
define("TNEF_BYTE", 0x00060000);
|
||||
define("TNEF_WORD", 0x00070000);
|
||||
define("TNEF_DWORD", 0x00080000);
|
||||
define("TNEF_MAX", 0x00090000);
|
||||
|
||||
define("TNEF_AIDOWNER", TNEF_LONG | 0x0008);
|
||||
define("TNEF_AREQUESTRES", TNEF_SHORT | 0x0009);
|
||||
define("TNEF_AFROM", TNEF_TRIPLES | 0x8000);
|
||||
define("TNEF_ASUBJECT", TNEF_STRING | 0x8004);
|
||||
define("TNEF_ADATESENT", TNEF_DATE | 0x8005);
|
||||
define("TNEF_ADATERECEIVED", TNEF_DATE | 0x8006);
|
||||
define("TNEF_ASTATUS", TNEF_BYTE | 0x8007);
|
||||
define("TNEF_AMCLASS", TNEF_WORD | 0x8008);
|
||||
define("TNEF_AMESSAGEID", TNEF_STRING | 0x8009);
|
||||
define("TNEF_ABODYTEXT", TNEF_TEXT | 0x800c);
|
||||
define("TNEF_APRIORITY", TNEF_SHORT | 0x800d);
|
||||
define("TNEF_ATTACHDATA", TNEF_BYTE | 0x800f);
|
||||
define("TNEF_AFILENAME", TNEF_STRING | 0x8010);
|
||||
define("TNEF_ATTACHMETAFILE", TNEF_BYTE | 0x8011);
|
||||
define("TNEF_AATTACHCREATEDATE", TNEF_DATE | 0x8012);
|
||||
define("TNEF_AATTACHMODDATE", TNEF_DATE | 0x8013);
|
||||
define("TNEF_ADATEMODIFIED", TNEF_DATE | 0x8020);
|
||||
define("TNEF_ARENDDATA", TNEF_BYTE | 0x9002);
|
||||
define("TNEF_AMAPIPROPS", TNEF_BYTE | 0x9003);
|
||||
define("TNEF_AMAPIATTRS", TNEF_BYTE | 0x9005);
|
||||
define("TNEF_AVERSION", TNEF_DWORD | 0x9006);
|
||||
define("TNEF_AOEMCODEPAGE", TNEF_BYTE | 0x9007);
|
||||
|
||||
define("TNEF_MAPI_NULL", 0x0001);
|
||||
define("TNEF_MAPI_SHORT", 0x0002);
|
||||
define("TNEF_MAPI_INT", 0x0003);
|
||||
define("TNEF_MAPI_FLOAT", 0x0004);
|
||||
define("TNEF_MAPI_DOUBLE", 0x0005);
|
||||
define("TNEF_MAPI_CURRENCY", 0x0006);
|
||||
define("TNEF_MAPI_APPTIME", 0x0007);
|
||||
define("TNEF_MAPI_ERROR", 0x000a);
|
||||
define("TNEF_MAPI_BOOLEAN", 0x000b);
|
||||
define("TNEF_MAPI_OBJECT", 0x000d);
|
||||
define("TNEF_MAPI_INT8BYTE", 0x0014);
|
||||
define("TNEF_MAPI_STRING", 0x001e);
|
||||
define("TNEF_MAPI_UNICODE_STRING", 0x001f);
|
||||
define("TNEF_MAPI_SYSTIME", 0x0040);
|
||||
define("TNEF_MAPI_CLSID", 0x0048);
|
||||
define("TNEF_MAPI_BINARY", 0x0102);
|
||||
|
||||
define("TNEF_MAPI_MV_FLAG", 0x1000);
|
||||
define("TNEF_MAPI_NAMED_TYPE_ID", 0x0000);
|
||||
define("TNEF_MAPI_NAMED_TYPE_STRING", 0x0001);
|
||||
|
||||
define("TNEF_MAPI_SUBJECT_PREFIX", 0x003D);
|
||||
define("TNEF_MAPI_SENT_REP_NAME", 0x0042);
|
||||
define("TNEF_MAPI_ORIGINAL_AUTHOR", 0x004D);
|
||||
define("TNEF_MAPI_SENT_REP_ADDRTYPE", 0x0064);
|
||||
define("TNEF_MAPI_SENT_REP_EMAIL_ADDR", 0x0065);
|
||||
define("TNEF_MAPI_CONVERSATION_TOPIC", 0x0070);
|
||||
define("TNEF_MAPI_SENDER_NAME", 0x0c1A);
|
||||
define("TNEF_MAPI_SENDER_ADDRTYPE", 0x0c1E);
|
||||
define("TNEF_MAPI_SENDER_EMAIL_ADDRESS", 0x0c1F);
|
||||
define("TNEF_MAPI_NORMALIZED_SUBJECT", 0x0E1D);
|
||||
define("TNEF_MAPI_ATTACH_SIZE", 0x0E20);
|
||||
define("TNEF_MAPI_ATTACH_NUM", 0x0E21);
|
||||
define("TNEF_MAPI_ACCESS_LEVEL", 0x0FF7);
|
||||
define("TNEF_MAPI_MAPPING_SIGNATURE", 0x0FF8);
|
||||
define("TNEF_MAPI_RECORD_KEY", 0x0FF9);
|
||||
define("TNEF_MAPI_STORE_RECORD_KEY", 0x0FFA);
|
||||
define("TNEF_MAPI_STORE_ENTRY_ID", 0x0FFB);
|
||||
define("TNEF_MAPI_OBJECT_TYPE", 0x0FFE);
|
||||
define("TNEF_MAPI_BODY", 0x1000);
|
||||
define("TNEF_MAPI_RTF_SYNC_BODY_TAG", 0x1008);
|
||||
define("TNEF_MAPI_RTF_COMPRESSED", 0x1009);
|
||||
define("TNEF_MAPI_BODY_HTML", 0x1013);
|
||||
define("TNEF_MAPI_NATIVE_BODY", 0x1016);
|
||||
define("TNEF_MAPI_DISPLAY_NAME", 0x3001);
|
||||
define("TNEF_MAPI_CREATION_TIME", 0x3007);
|
||||
define("TNEF_MAPI_MODIFICATION_TIME", 0x3008);
|
||||
define("TNEF_MAPI_ATTACH_DATA", 0x3701);
|
||||
define("TNEF_MAPI_ATTACH_ENCODING", 0x3702);
|
||||
define("TNEF_MAPI_ATTACH_EXTENSION", 0x3703);
|
||||
define("TNEF_MAPI_ATTACH_METHOD", 0x3705);
|
||||
define("TNEF_MAPI_ATTACH_LONG_FILENAME", 0x3707);
|
||||
define("TNEF_MAPI_RENDERING_POSITION", 0x370B);
|
||||
define("TNEF_MAPI_ATTACH_MIME_TAG", 0x370E);
|
||||
define("TNEF_MAPI_ACCOUNT", 0x3A00);
|
||||
define("TNEF_MAPI_GENERATION", 0x3A05);
|
||||
define("TNEF_MAPI_GIVEN_NAME", 0x3A06);
|
||||
define("TNEF_MAPI_BUSINESS_TEL_NUMBER", 0x3A08);
|
||||
define("TNEF_MAPI_HOME_TEL_NUMBER", 0x3A09);
|
||||
define("TNEF_MAPI_INITIALS", 0x3A0A);
|
||||
define("TNEF_MAPI_KEYWORDS", 0x3A0B);
|
||||
define("TNEF_MAPI_LANGUAGE", 0x3A0C);
|
||||
define("TNEF_MAPI_LOCATION", 0x3A0D);
|
||||
define("TNEF_MAPI_SURNAME", 0x3A11);
|
||||
define("TNEF_MAPI_POSTAL_ADDRESS", 0x3A15);
|
||||
define("TNEF_MAPI_COMPANY_NAME", 0x3A16);
|
||||
define("TNEF_MAPI_TITLE", 0x3A17);
|
||||
define("TNEF_MAPI_DEPARTMENT_NAME", 0x3A18);
|
||||
define("TNEF_MAPI_OFFICE_LOCATION", 0x3A19);
|
||||
define("TNEF_MAPI_PRIMARY_TEL_NUMBER", 0x3A1A);
|
||||
define("TNEF_MAPI_BUSINESS2_TEL_NUMBER", 0x3A1B);
|
||||
define("TNEF_MAPI_MOBILE_TEL_NUMBER", 0x3A1C);
|
||||
define("TNEF_MAPI_RADIO_TEL_NUMBER", 0x3A1D);
|
||||
define("TNEF_MAPI_CAR_TEL_NUMBER", 0x3A1E);
|
||||
define("TNEF_MAPI_OTHER_TEL_NUMBER", 0x3A1F);
|
||||
define("TNEF_MAPI_PAGER_TEL_NUMBER", 0x3A21);
|
||||
define("TNEF_MAPI_PRIMARY_FAX_NUMBER", 0x3A23);
|
||||
define("TNEF_MAPI_BUSINESS_FAX_NUMBER", 0x3A24);
|
||||
define("TNEF_MAPI_HOME_FAX_NUMBER", 0x3A25);
|
||||
define("TNEF_MAPI_COUNTRY", 0x3A26);
|
||||
define("TNEF_MAPI_LOCALTY", 0x3A27);
|
||||
define("TNEF_MAPI_STATE_OR_PROVINCE", 0x3A28);
|
||||
define("TNEF_MAPI_STREET_ADDRESS", 0x3A29);
|
||||
define("TNEF_MAPI_POSTAL_CODE", 0x3A2A);
|
||||
define("TNEF_MAPI_POST_OFFICE_BOX", 0x3A2B);
|
||||
define("TNEF_MAPI_TELEX_NUMBER", 0x3A2C);
|
||||
define("TNEF_MAPI_ISDN_NUMBER", 0x3A2D);
|
||||
define("TNEF_MAPI_ASSISTANT_TEL_NUMBER", 0x3A2E);
|
||||
define("TNEF_MAPI_HOME2_TEL_NUMBER", 0x3A2F);
|
||||
define("TNEF_MAPI_ASSISTANT", 0x3A30);
|
||||
define("TNEF_MAPI_MIDDLE_NAME", 0x3A44);
|
||||
define("TNEF_MAPI_DISPLAYNAME_PREFIX", 0x3A45);
|
||||
define("TNEF_MAPI_PROFESSION", 0x3A46);
|
||||
define("TNEF_MAPI_SPOUSE_NAME", 0x3A48);
|
||||
define("TNEF_MAPI_MANAGER_NAME", 0x3A4E);
|
||||
define("TNEF_MAPI_NICKNAME", 0x3A4F);
|
||||
define("TNEF_MAPI_PERSONAL_HOME_PAGE", 0x3A50);
|
||||
define("TNEF_MAPI_BUSINESS_HOME_PAGE", 0x3A51);
|
||||
define("TNEF_MAPI_CONTACT_EMAIL_ADDR", 0x3A56);
|
||||
define("TNEF_MAPI_HOME_ADDR_CITY", 0x3A59);
|
||||
define("TNEF_MAPI_HOME_ADDR_COUNTRY", 0x3A5A);
|
||||
define("TNEF_MAPI_HOME_ADDR_ZIP", 0x3A5B);
|
||||
define("TNEF_MAPI_HOME_ADDR_STATE", 0x3A5C);
|
||||
define("TNEF_MAPI_HOME_ADDR_STREET", 0x3A5D);
|
||||
define("TNEF_MAPI_HOME_ADDR_PO_BOX", 0x3A5E);
|
||||
define("TNEF_MAPI_OTHER_ADDR_CITY", 0x3A5F);
|
||||
define("TNEF_MAPI_OTHER_ADDR_COUNTRY", 0x3A60);
|
||||
define("TNEF_MAPI_OTHER_ADDR_ZIP", 0x3A61);
|
||||
define("TNEF_MAPI_OTHER_ADDR_STATE", 0x3A62);
|
||||
define("TNEF_MAPI_OTHER_ADDR_STREET", 0x3A63);
|
||||
define("TNEF_MAPI_OTHER_ADDR_PO_BOX", 0x3A64);
|
||||
|
||||
define("TNEF_MAPI_OTHER_HOME_PAGE", 0x804F);
|
||||
define("TNEF_MAPI_EMAIL1_DISPLAY", 0x8080);
|
||||
define("TNEF_MAPI_EMAIL1_TRANSPORT", 0x8082);
|
||||
define("TNEF_MAPI_EMAIL1_EMAIL", 0x8083);
|
||||
define("TNEF_MAPI_EMAIL1_EMAIL2", 0x8084);
|
||||
define("TNEF_MAPI_EMAIL2_DISPLAY", 0x8090);
|
||||
define("TNEF_MAPI_EMAIL2_TRANSPORT", 0x8092);
|
||||
define("TNEF_MAPI_EMAIL2_EMAIL", 0x8093);
|
||||
define("TNEF_MAPI_EMAIL2_EMAIL2", 0x8094);
|
||||
define("TNEF_MAPI_EMAIL3_DISPLAY", 0x80A0);
|
||||
define("TNEF_MAPI_EMAIL3_TRANSPORT", 0x80A2);
|
||||
define("TNEF_MAPI_EMAIL3_EMAIL", 0x80A3);
|
||||
define("TNEF_MAPI_EMAIL3_EMAIL2", 0x80A4);
|
||||
|
||||
|
||||
|
||||
// used in RTF
|
||||
//
|
||||
define("CRTF_UNCOMPRESSED", 0x414c454d);
|
||||
define("CRTF_COMPRESSED", 0x75465a4c);
|
||||
|
||||
|
||||
// used in VCARD
|
||||
//
|
||||
define ("EMAIL_DISPLAY", 1);
|
||||
define ("EMAIL_TRANSPORT", 2);
|
||||
define ("EMAIL_EMAIL", 3);
|
||||
define ("EMAIL_EMAIL2", 4);
|
||||
|
||||
define ("ADDRESS_STREET", "Street");
|
||||
define ("ADDRESS_ZIP", "Zip");
|
||||
define ("ADDRESS_CITY", "City");
|
||||
define ("ADDRESS_COUNTRY", "Country");
|
||||
define ("ADDRESS_STATE", "State");
|
||||
define ("ADDRESS_PO_BOX", "PO Box");
|
96
snappymail/v/0.0.0/app/libraries/TNEFDecoder/functions.php
Normal file
96
snappymail/v/0.0.0/app/libraries/TNEFDecoder/functions.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php namespace TNEFDecoder;
|
||||
|
||||
/**
|
||||
* SquirrelMail TNEF Decoder Plugin
|
||||
*
|
||||
* Copyright (c) 2010- Paul Lesniewski <paul@squirrelmail.org>
|
||||
* Copyright (c) 2003 Bernd Wiegmann <bernd@wib-software.de>
|
||||
* Copyright (c) 2002 Graham Norburys <gnorbury@bondcar.com>
|
||||
*
|
||||
* Licensed under the GNU GPL. For full terms see the file COPYING.
|
||||
*
|
||||
* @package plugins
|
||||
* @subpackage tnef_decoder
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Debugging output --> to a file (/tmp/squirrelmail_tnef_decoder.log)
|
||||
*
|
||||
* Note this assumes a world-writable /tmp directory
|
||||
*
|
||||
* @param string $string The text to be logged
|
||||
*
|
||||
* @return boolean TRUE on success, FALSE when a problem occurred
|
||||
*
|
||||
*/
|
||||
function tnef_log($string)
|
||||
{
|
||||
\SnappyMail\Log::debug('TNEF', $string);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TNEF decoding helper function
|
||||
*
|
||||
*/
|
||||
function tnef_getx($size, TNEFBuffer $buf)
|
||||
{
|
||||
return $buf->getBytes($size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TNEF decoding helper function
|
||||
*
|
||||
*/
|
||||
function tnef_geti8(TNEFBuffer $buf)
|
||||
{
|
||||
$bytes = $buf->getBytes(1, $buf);
|
||||
if ($bytes === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ord($bytes[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TNEF decoding helper function
|
||||
*
|
||||
*/
|
||||
function tnef_geti16(TNEFBuffer $buf): ?int
|
||||
{
|
||||
$bytes = $buf->getBytes(2, $buf);
|
||||
if ($bytes === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ord($bytes[0])
|
||||
+ (ord($bytes[1]) << 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TNEF decoding helper function
|
||||
*
|
||||
*/
|
||||
function tnef_geti32(TNEFBuffer $buf): ?int
|
||||
{
|
||||
$bytes = $buf->getBytes(4, $buf);
|
||||
if ($bytes === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ord($bytes[0])
|
||||
+ (ord($bytes[1]) << 8)
|
||||
+ (ord($bytes[2]) << 16)
|
||||
+ (ord($bytes[3]) << 24);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class ControlSymbol implements Entity
|
||||
{
|
||||
public string $symbol;
|
||||
public /*int|string*/ $parameter;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class ControlWord implements Entity
|
||||
{
|
||||
public string $word;
|
||||
public int $parameter;
|
||||
}
|
338
snappymail/v/0.0.0/app/libraries/snappymail/rtf/document.php
Normal file
338
snappymail/v/0.0.0/app/libraries/snappymail/rtf/document.php
Normal file
|
@ -0,0 +1,338 @@
|
|||
<?php
|
||||
/**
|
||||
* Based on https://github.com/henck/rtf-html-php
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class Document
|
||||
{
|
||||
private string $rtf; // RTF string being parsed
|
||||
private int $len = 0; // Length of RTF string
|
||||
private int $pos = 0; // Current position in RTF string
|
||||
public ?Group $root = null; // Root group
|
||||
private ?Group $group = null; // Current group
|
||||
|
||||
private string $char = '';
|
||||
private array $uc = [];
|
||||
|
||||
private static array $groups = [
|
||||
'colortbl' => Groups\ColorTable::class,
|
||||
'filetbl' => Groups\FileTable::class,
|
||||
'fonttbl' => Groups\FontTable::class,
|
||||
'generator' => Groups\Generator::class,
|
||||
'info' => Groups\Info::class,
|
||||
'listtable' => Groups\ListTable::class,
|
||||
'listtext' => Groups\ListText::class,
|
||||
'listoverridetable' => Groups\ListOverrideTable::class,
|
||||
'pict' => Groups\Pict::class,
|
||||
'stylesheet' => Groups\StyleSheet::class
|
||||
];
|
||||
|
||||
public function __construct(string $rtf)
|
||||
{
|
||||
/*
|
||||
* Attempt to parse an RTF string.
|
||||
*/
|
||||
$this->rtf = $rtf;
|
||||
$this->len = \strlen($rtf);
|
||||
$this->pos = 0;
|
||||
$this->root = null;
|
||||
$this->group = null;
|
||||
$this->char = '';
|
||||
$this->uc = [];
|
||||
|
||||
while ($this->pos < $this->len-1) {
|
||||
// Read next character:
|
||||
$this->nextChar();
|
||||
|
||||
// Ignore \r and \n
|
||||
if ($this->is_endofline()) continue;
|
||||
|
||||
// What type of character is this?
|
||||
switch ($this->char)
|
||||
{
|
||||
// Group handling
|
||||
case '{':
|
||||
$this->startGroup();
|
||||
break;
|
||||
case '}':
|
||||
$this->endGroup();
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
// If there is no current group, then this is not a valid RTF file.
|
||||
if (!$this->group) {
|
||||
throw new \RuntimeException('Parse error: RTF text outside of group.');
|
||||
}
|
||||
// Beginning of an RTF control word or control symbol.
|
||||
// Look ahead by one character to see if it starts with
|
||||
// a letter (control word) or another symbol (control symbol):
|
||||
$this->group->children[] = \ctype_alpha($this->nextChar(true))
|
||||
? $this->parseControlWord()
|
||||
: $this->parseControlSymbol();
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->parseText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->rtf = '';
|
||||
$this->group = null;
|
||||
}
|
||||
|
||||
// Get the next character from the RTF stream.
|
||||
// Parsing is aborted when reading beyond end of input string.
|
||||
protected function nextChar(bool $peek = false): string
|
||||
{
|
||||
if ($this->pos >= $this->len) {
|
||||
$this->char = null;
|
||||
throw new \RuntimeException("Parse error: Tried to read past end of input; RTF is probably truncated.");
|
||||
}
|
||||
if ($peek) {
|
||||
return $this->rtf[$this->pos];
|
||||
}
|
||||
$this->char = $this->rtf[$this->pos++];
|
||||
return $this->char;
|
||||
}
|
||||
|
||||
/*
|
||||
* (Helper method)
|
||||
* Is the current character end-of-line (EOL)?
|
||||
*/
|
||||
protected function is_endofline(): bool
|
||||
{
|
||||
if (\strspn($this->char, "\r\n")) {
|
||||
// Checks for a Windows/Acron type EOL
|
||||
if (\strspn($this->nextChar(true), "\r\n")) {
|
||||
++$this->pos;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store state of document on stack.
|
||||
protected function startGroup()
|
||||
{
|
||||
$group = new Group();
|
||||
if ('\\' === $this->nextChar(true)) {
|
||||
$cword = null;
|
||||
if (\ctype_alpha($this->rtf[$this->pos+1])) {
|
||||
$this->nextChar();
|
||||
$cword = $this->parseControlWord();
|
||||
// Replace group with special group?
|
||||
if (isset(static::$groups[$cword->word])) {
|
||||
$group = new static::$groups[$cword->word]();
|
||||
} else {
|
||||
$group->children[] = $cword;
|
||||
}
|
||||
}
|
||||
else if ('*' === $this->rtf[$this->pos+1]) {
|
||||
$group = new Groups\Destination();
|
||||
$this->nextChar();
|
||||
$this->parseControlSymbol();
|
||||
// $group->children[] = $this->parseControlSymbol();
|
||||
if (\ctype_alpha($this->rtf[$this->pos+1])) {
|
||||
/**
|
||||
* Destinations added after the 1987 RTF Specification may be preceded by the control symbol \*
|
||||
* This control symbol identifies destinations whose related text should be
|
||||
* ignored if the RTF reader does not recognize the destination control word.
|
||||
*/
|
||||
$this->nextChar();
|
||||
$cword = $this->parseControlWord();
|
||||
// Replace group with special group?
|
||||
if (isset(static::$groups[$cword->word])) {
|
||||
$group = new static::$groups[$cword->word]();
|
||||
} else {
|
||||
$group->children[] = $cword;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is there a current group? Then make the new group its child:
|
||||
if ($this->group) {
|
||||
$group->parent = $this->group;
|
||||
$this->group->children[] = $group;
|
||||
$this->uc[] = \end($this->uc);
|
||||
}
|
||||
// If there is no parent group, then set this group
|
||||
// as the root group.
|
||||
else {
|
||||
$this->root = $group;
|
||||
// Create uc stack and insert the first default value
|
||||
$this->uc = [1];
|
||||
}
|
||||
// Set the new group as the current group:
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
// Retrieve state of document from stack.
|
||||
protected function endGroup()
|
||||
{
|
||||
$this->group = $this->group->parent;
|
||||
\array_pop($this->uc);
|
||||
}
|
||||
|
||||
protected function parseControlSymbol(): Entity
|
||||
{
|
||||
// Read symbol (one character only).
|
||||
$symbol = $this->nextChar();
|
||||
|
||||
// Exceptional case:
|
||||
// Treat EOL symbols as \par control word
|
||||
if ($this->is_endofline()) {
|
||||
$cword = new ControlWord();
|
||||
$cword->word = 'par';
|
||||
$cword->parameter = 0;
|
||||
return $cword;
|
||||
}
|
||||
|
||||
// Symbols ordinarily have no parameter. However,
|
||||
// if this is \' (a single quote), then it is
|
||||
// followed by a 2-digit hex-code:
|
||||
$parameter = 0;
|
||||
if ('\'' === $symbol) {
|
||||
$parameter = \hexdec($this->nextChar() . $this->nextChar());
|
||||
}
|
||||
|
||||
// Add new control symbol as a child to the current group:
|
||||
$csymbol = new ControlSymbol();
|
||||
$csymbol->symbol = $symbol;
|
||||
$csymbol->parameter = $parameter;
|
||||
return $csymbol;
|
||||
}
|
||||
|
||||
protected function parseControlWord(): ControlWord
|
||||
{
|
||||
// Read letters until a non-letter is reached.
|
||||
$word = '';
|
||||
$this->nextChar();
|
||||
while (\ctype_alpha($this->char)) {
|
||||
$word .= $this->char;
|
||||
$this->nextChar();
|
||||
}
|
||||
|
||||
// Read parameter (if any) consisting of digits.
|
||||
// Parameter may be negative, i.e., starting with a '-'
|
||||
$negative = false;
|
||||
if ('-' === $this->char) {
|
||||
$this->nextChar();
|
||||
$negative = true;
|
||||
}
|
||||
$parameter = null;
|
||||
while (\ctype_digit($this->char)) {
|
||||
if (!$parameter) $parameter = 0;
|
||||
$parameter = $parameter * 10 + (int) $this->char;
|
||||
$this->nextChar();
|
||||
}
|
||||
// If no parameter present, assume control word's default (usually 1)
|
||||
// If no default then assign 0 to the parameter
|
||||
if (null === $parameter) $parameter = 1;
|
||||
|
||||
// Convert parameter to a negative number when applicable
|
||||
if ($negative) $parameter = -$parameter;
|
||||
|
||||
// Update uc value
|
||||
if ('uc' === $word) {
|
||||
\array_pop($this->uc);
|
||||
$this->uc[] = $parameter;
|
||||
}
|
||||
|
||||
// Skip space delimiter
|
||||
if (' ' !== $this->char && !$this->is_endofline()) {
|
||||
--$this->pos;
|
||||
}
|
||||
|
||||
// If this is \u, then the parameter will be followed
|
||||
// by {$this->uc} characters.
|
||||
if ('u' === $word) {
|
||||
// Convert parameter to unsigned decimal unicode
|
||||
if ($negative) {
|
||||
$parameter = 65536 + $parameter;
|
||||
}
|
||||
|
||||
// Will ignore replacement characters $uc times
|
||||
$uc = \end($this->uc);
|
||||
while (0 < $uc--) {
|
||||
$this->nextChar();
|
||||
// If the replacement character is encoded as
|
||||
// hexadecimal value \'hh then jump over it
|
||||
if ('\\' === $this->char && '\'' === $this->rtf[$this->pos])
|
||||
$this->pos = $this->pos + 3;
|
||||
|
||||
// Break if it's an RTF scope delimiter
|
||||
else if (\strspn($this->char, '{}'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$cword = new ControlWord();
|
||||
$cword->word = $word;
|
||||
$cword->parameter = $parameter;
|
||||
return $cword;
|
||||
}
|
||||
|
||||
protected function parseText(): void
|
||||
{
|
||||
// If there is no current group, then this is not a valid RTF file.
|
||||
if (!$this->group) {
|
||||
throw new \RuntimeException('Parse error: RTF text outside of group.');
|
||||
}
|
||||
|
||||
// Parse plain text up to backslash or brace,
|
||||
// unless escaped.
|
||||
$text = '';
|
||||
$terminate = false;
|
||||
do
|
||||
{
|
||||
// Ignore EOL characters
|
||||
if ($this->is_endofline()) {
|
||||
$this->nextChar();
|
||||
continue;
|
||||
}
|
||||
// Is this an escape?
|
||||
if ('\\' == $this->char) {
|
||||
// Perform lookahead to see if this
|
||||
// is really an escape sequence.
|
||||
$this->nextChar();
|
||||
switch ($this->char)
|
||||
{
|
||||
case '\\': break;
|
||||
case '{': break;
|
||||
case '}': break;
|
||||
default:
|
||||
// Not an escape. Roll back.
|
||||
$this->pos = $this->pos - 2;
|
||||
$terminate = true;
|
||||
break;
|
||||
}
|
||||
} else if (\strspn($this->char, '{}')) {
|
||||
--$this->pos;
|
||||
$terminate = true;
|
||||
}
|
||||
|
||||
if (!$terminate) {
|
||||
// Save plain text
|
||||
$text .= $this->char;
|
||||
$this->nextChar();
|
||||
}
|
||||
}
|
||||
while (!$terminate && $this->pos < $this->len);
|
||||
|
||||
// Create new Text element:
|
||||
$text = new Text($text);
|
||||
|
||||
// Add text as a child to the current group:
|
||||
$this->group->children[] = $text;
|
||||
}
|
||||
|
||||
public function toHTML(): string
|
||||
{
|
||||
return (new HtmlFormatter())->format($this);
|
||||
}
|
||||
}
|
90
snappymail/v/0.0.0/app/libraries/snappymail/rtf/encoding.php
Normal file
90
snappymail/v/0.0.0/app/libraries/snappymail/rtf/encoding.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
abstract class Encoding
|
||||
{
|
||||
public static function getFromCharset(int $fcharset): ?string
|
||||
{
|
||||
/* maps windows character sets to iconv encoding names */
|
||||
$charset = array (
|
||||
0 => 'CP1252', // ANSI: Western Europe
|
||||
1 => 'CP1252', //*Default
|
||||
2 => 'CP1252', //*Symbol
|
||||
3 => null, // Invalid
|
||||
77 => 'MAC', //*also [MacRoman]: Macintosh
|
||||
128 => 'CP932', //*or [Shift_JIS]?: Japanese
|
||||
129 => 'CP949', //*also [UHC]: Korean (Hangul)
|
||||
130 => 'CP1361', //*also [JOHAB]: Korean (Johab)
|
||||
134 => 'CP936', //*or [GB2312]?: Simplified Chinese
|
||||
136 => 'CP950', //*or [BIG5]?: Traditional Chinese
|
||||
161 => 'CP1253', // Greek
|
||||
162 => 'CP1254', // Turkish (latin 5)
|
||||
163 => 'CP1258', // Vietnamese
|
||||
177 => 'CP1255', // Hebrew
|
||||
178 => 'CP1256', // Simplified Arabic
|
||||
179 => 'CP1256', //*Traditional Arabic
|
||||
180 => 'CP1256', //*Arabic User
|
||||
181 => 'CP1255', //*Hebrew User
|
||||
186 => 'CP1257', // Baltic
|
||||
204 => 'CP1251', // Russian (Cyrillic)
|
||||
222 => 'CP874', // Thai
|
||||
238 => 'CP1250', // Eastern European (latin 2)
|
||||
254 => 'CP437', //*also [IBM437][437]: PC437
|
||||
255 => 'CP437' //*OEM still PC437
|
||||
);
|
||||
|
||||
if (isset($charset[$fcharset])) {
|
||||
return $charset[$fcharset];
|
||||
}
|
||||
\trigger_error("Unknown charset: {$fcharset}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getFromCodepage($cpg): ?string
|
||||
{
|
||||
$codePage = array (
|
||||
'ansi' => 'CP1252',
|
||||
'mac' => 'MAC',
|
||||
'pc' => 'CP437',
|
||||
'pca' => 'CP850',
|
||||
437 => 'CP437', // United States IBM
|
||||
708 => 'ASMO-708', // also [ISO-8859-6][ARABIC] Arabic
|
||||
/* Not supported by iconv
|
||||
709, => '' // Arabic (ASMO 449+, BCON V4)
|
||||
710, => '' // Arabic (transparent Arabic)
|
||||
711, => '' // Arabic (Nafitha Enhanced)
|
||||
720, => '' // Arabic (transparent ASMO)
|
||||
*/
|
||||
819 => 'CP819', // Windows 3.1 (US and Western Europe)
|
||||
850 => 'CP850', // IBM multilingual
|
||||
852 => 'CP852', // Eastern European
|
||||
860 => 'CP860', // Portuguese
|
||||
862 => 'CP862', // Hebrew
|
||||
863 => 'CP863', // French Canadian
|
||||
864 => 'CP864', // Arabic
|
||||
865 => 'CP865', // Norwegian
|
||||
866 => 'CP866', // Soviet Union
|
||||
874 => 'CP874', // Thai
|
||||
932 => 'CP932', // Japanese
|
||||
936 => 'CP936', // Simplified Chinese
|
||||
949 => 'CP949', // Korean
|
||||
950 => 'CP950', // Traditional Chinese
|
||||
1250 => 'CP1250', // Windows 3.1 (Eastern European)
|
||||
1251 => 'CP1251', // Windows 3.1 (Cyrillic)
|
||||
1252 => 'CP1252', // Western European
|
||||
1253 => 'CP1253', // Greek
|
||||
1254 => 'CP1254', // Turkish
|
||||
1255 => 'CP1255', // Hebrew
|
||||
1256 => 'CP1256', // Arabic
|
||||
1257 => 'CP1257', // Baltic
|
||||
1258 => 'CP1258', // Vietnamese
|
||||
1361 => 'CP1361'); // Johab
|
||||
|
||||
if (isset($codePage[$cpg])) {
|
||||
return $codePage[$cpg];
|
||||
}
|
||||
\trigger_error("Unknown codepage: {$cpg}");
|
||||
return null;
|
||||
}
|
||||
}
|
11
snappymail/v/0.0.0/app/libraries/snappymail/rtf/entity.php
Normal file
11
snappymail/v/0.0.0/app/libraries/snappymail/rtf/entity.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
/*
|
||||
* Entity is the parent class of all RTF entities,
|
||||
* like Group, ControlWord and ControlSymbol.
|
||||
*/
|
||||
interface Entity
|
||||
{
|
||||
}
|
79
snappymail/v/0.0.0/app/libraries/snappymail/rtf/fontinfo.php
Normal file
79
snappymail/v/0.0.0/app/libraries/snappymail/rtf/fontinfo.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class FontInfo implements \Stringable
|
||||
{
|
||||
public string
|
||||
$family = '',
|
||||
$name = '',
|
||||
$charset = '',
|
||||
$codepage = '';
|
||||
public int
|
||||
$uid = 0,
|
||||
$fprq = 0; // 0 = Default pitch, 1 = Fixed pitch, 2 = Variable pitch
|
||||
|
||||
public function addEntity(Entity $entity): void
|
||||
{
|
||||
if ($entity instanceof ControlWord){
|
||||
switch ($entity->word) {
|
||||
case 'f':
|
||||
$this->uid = $entity->parameter;
|
||||
break;
|
||||
|
||||
// <fontfamily>
|
||||
case 'froman': $this->family = 'serif'; break;
|
||||
case 'fswiss': $this->family = 'sans-serif'; break;
|
||||
case 'fmodern': $this->family = 'monospace'; break;
|
||||
case 'fscript': $this->family = 'cursive'; break;
|
||||
case 'fdecor': $this->family = 'fantasy'; break;
|
||||
// case 'fnil': break; // default font
|
||||
// case 'ftech': break; // symbol
|
||||
// case 'fbidi': break; // bidirectional font
|
||||
|
||||
case 'fcharset':
|
||||
$this->charset = Encoding::getFromCharset($entity->parameter);
|
||||
break;
|
||||
|
||||
case 'fprq':
|
||||
$this->fprq = $entity->parameter;
|
||||
break;
|
||||
|
||||
case 'cpg':
|
||||
$this->codepage = Encoding::getFromCodepage($entity->parameter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// <fontname>
|
||||
else if ($entity instanceof Text) {
|
||||
// Store font name (except ; delimiter at end)
|
||||
$this->name = \substr($entity->text, 0, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
else if ($entity instanceof Group) {
|
||||
// possible subgroups:
|
||||
// <panose>
|
||||
// <nontaggedname>
|
||||
// <fontaltname>
|
||||
// <fontemb>
|
||||
// <fontfname>
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$list = [];
|
||||
if ($this->name) {
|
||||
// If the name has double quotes replace them with single quotes
|
||||
$this->name = \str_replace('"', "'", $this->name);
|
||||
$list[] = $this->name;
|
||||
}
|
||||
if ($this->family) {
|
||||
$list[] = $this->family;
|
||||
}
|
||||
return $list ? 'font-family:' . \implode(', ', $list) : '';
|
||||
}
|
||||
}
|
32
snappymail/v/0.0.0/app/libraries/snappymail/rtf/group.php
Normal file
32
snappymail/v/0.0.0/app/libraries/snappymail/rtf/group.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class Group implements Entity
|
||||
{
|
||||
public ?Group $parent = null;
|
||||
public array $children = []; // Entities
|
||||
|
||||
public function getType()
|
||||
{
|
||||
if (isset($this->children[0])) {
|
||||
$child = $this->children[0];
|
||||
|
||||
// If the first child is a control word, then
|
||||
// the group type is the word.
|
||||
if ($child instanceof ControlWord) {
|
||||
return $child->word;
|
||||
}
|
||||
|
||||
// If the first child is a control symbol, then
|
||||
// the group type is * for a special symbol, or null.
|
||||
if ($child instanceof ControlSymbol) {
|
||||
return ('*' === $child->symbol) ? '*' : null;
|
||||
}
|
||||
}
|
||||
|
||||
// If first child is neither word nor symbol, then
|
||||
// group type is null.
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
use SnappyMail\Rtf\ControlWord;
|
||||
use SnappyMail\Rtf\Group;
|
||||
use SnappyMail\Rtf\Text;
|
||||
|
||||
/**
|
||||
* <colortbl> '{' \colortbl <colordef>+ '}'
|
||||
* <colordef> <themecolor>? & \ctintN? & \cshadeN? \redN? & \greenN? & \blueN? ';'
|
||||
* <themecolor> \cmaindarkone | \cmainlightone | \cmaindarktwo | \cmainlighttwo |
|
||||
* \caccentone | \caccenttwo | \caccentthree | \caccentfour | \caccentfive |
|
||||
* \caccentsix | \chyperlink | \cfollowedhyperlink | \cbackgroundone |
|
||||
* \ctextone | \cbackgroundtwo | \ctexttwo
|
||||
*/
|
||||
class ColorTable extends Group
|
||||
{
|
||||
private static $themecolors = [
|
||||
'cmaindarkone', 'cmainlightone', 'cmaindarktwo', 'cmainlighttwo',
|
||||
'caccentone', 'caccenttwo', 'caccentthree', 'caccentfour', 'caccentfive',
|
||||
'caccentsix', 'chyperlink', 'cfollowedhyperlink', 'cbackgroundone',
|
||||
'ctextone', 'cbackgroundtwo', 'ctexttwo'
|
||||
];
|
||||
|
||||
public function getColors(): array
|
||||
{
|
||||
$colortbl = [
|
||||
null
|
||||
// 'inherit'
|
||||
// 'initial'
|
||||
// 'unset'
|
||||
];
|
||||
$color = [];
|
||||
foreach ($this->children as $i => $entity) {
|
||||
if ($entity instanceof ControlWord) {
|
||||
if (isset(static::$themecolors[$entity->word])) {
|
||||
$color['themecolor'] = $entity->word;
|
||||
} else {
|
||||
$color[$entity->word] = $entity->parameter;
|
||||
}
|
||||
} else if ($entity instanceof Text) {
|
||||
// This is a delimiter ';' === $entity->text
|
||||
if ($i) {
|
||||
if (isset($color['red'])) {
|
||||
$colortbl[] = "rgb({$color['red']},{$color['green']},{$color['blue']})";
|
||||
// hex string format
|
||||
// $colortbl[] = \sprintf('#%02x%02x%02x', $color['red'], $color['green'], $color['blue']);
|
||||
} else if (isset($color['themecolor'])) {
|
||||
$colortbl[] = "var(--rtf-{$color['themecolor']})";
|
||||
}
|
||||
}
|
||||
$color = [];
|
||||
}
|
||||
}
|
||||
return $colortbl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class Destination extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class FileTable extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
use SnappyMail\Rtf\FontInfo;
|
||||
use SnappyMail\Rtf\Group;
|
||||
|
||||
/**
|
||||
* <fonttbl> '{' \fonttbl (<fontinfo> | ('{' <fontinfo> '}'))+ '}'
|
||||
* <fontinfo> <themefont>? \fN <fontfamily> \fcharsetN? \fprq? <panose>? <nontaggedname>? <fontemb>? \cpgN? <fontname> <fontaltname>? ';'
|
||||
* <themefont> \flomajor | \fhimajor | \fdbmajor | \fbimajor | \flominor | \fhiminor | \fdbminor | \fbiminor
|
||||
* <fontfamily> \fnil | \froman | \fswiss | \fmodern | \fscript | \fdecor | \ftech | \fbidi
|
||||
* <panose> '{\*' \panose <data> '}'
|
||||
* <nontaggedname> '{\*' \fname #PCDATA ';}'
|
||||
* <fontname> #PCDATA
|
||||
* <fontaltname> '{\*' \falt #PCDATA '}'
|
||||
* <fontemb> '{\*' \fontemb <fonttype> <fontfname>? <data>? '}'
|
||||
* <fonttype> \ftnil | \fttruetype
|
||||
* <fontfname> '{\*' \fontfile \cpgN? #PCDATA '}'
|
||||
*/
|
||||
class FontTable extends Group
|
||||
{
|
||||
public function getFonts(): array
|
||||
{
|
||||
$fonts = [];
|
||||
$basefont = null;
|
||||
foreach ($this->children as $fontinfo) {
|
||||
if ($fontinfo instanceof Group) {
|
||||
$font = new FontInfo();
|
||||
foreach ($fontinfo->children as $entity) {
|
||||
$font->addEntity($entity);
|
||||
}
|
||||
$fonts[$font->uid] = $font;
|
||||
} else {
|
||||
$basefont = $basefont ?: new FontInfo();
|
||||
$basefont->addEntity($fontinfo);
|
||||
}
|
||||
}
|
||||
if ($basefont && !$fonts) {
|
||||
$fonts[$basefont->uid] = $basefont;
|
||||
}
|
||||
return $fonts;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class Generator extends Destination
|
||||
{
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class Info extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class ListOverrideTable extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class ListTable extends Destination
|
||||
{
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class ListText extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
use SnappyMail\Rtf\ControlWord;
|
||||
use SnappyMail\Rtf\Group;
|
||||
use SnappyMail\Rtf\Text;
|
||||
|
||||
class Pict extends Group
|
||||
{
|
||||
public function getHTML(): string
|
||||
{
|
||||
$format = 'bmp';
|
||||
$style = [];
|
||||
$binarySize = 0; // Number of bytes of the binary data
|
||||
$data = null; // Binary or Hexadecimal Data
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
if ($child instanceof ControlWord) {
|
||||
switch ($child->word) {
|
||||
// Picture Format
|
||||
case 'emfblip':
|
||||
case 'pngblip':
|
||||
case 'jpegblip':
|
||||
$format = \substr($child->word, 0, -4);
|
||||
break;
|
||||
case 'macpict':
|
||||
$format = 'pict';
|
||||
break;
|
||||
// case 'wmetafile': $format = 'bmp'; break;
|
||||
|
||||
// Picture size and scaling
|
||||
case 'picw':
|
||||
$style[] = "width:{$child->parameter}px";
|
||||
break;
|
||||
case 'pich':
|
||||
$style[] = "height:{$child->parameter}px";
|
||||
break;
|
||||
case 'picscalex':
|
||||
// $style[] = "width:{$child->parameter}%";
|
||||
break;
|
||||
case 'picscaley':
|
||||
// $style[] = "height:{$child->parameter}%";
|
||||
break;
|
||||
|
||||
// Binary or Hexadecimal Data ?
|
||||
case 'bin': $binarySize = $child->parameter; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
} elseif ($child instanceof Text) { // store Data
|
||||
$data = $child->text;
|
||||
}
|
||||
}
|
||||
|
||||
if ($binarySize) {
|
||||
$data = \substr($data, 0, $binarySize);
|
||||
} else {
|
||||
$data = \pack('H*',$data);
|
||||
}
|
||||
|
||||
if ($style) {
|
||||
$style = ' style="' . \implode(';', $style) . '"';
|
||||
}
|
||||
|
||||
return "<img{$style} src=\"data:image/{$format};base64," . \base64_encode($data) . "\"/>";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf\Groups;
|
||||
|
||||
class StyleSheet extends \SnappyMail\Rtf\Group
|
||||
{
|
||||
}
|
|
@ -0,0 +1,583 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class HtmlFormatter
|
||||
{
|
||||
private string $output = '';
|
||||
private string $RTFencoding = '';
|
||||
private $defaultFont;
|
||||
|
||||
private ?Style $previousStyle = null;
|
||||
private array $styles = [];
|
||||
private ?Style $style = null;
|
||||
private array $openedTags = [];
|
||||
|
||||
public function format(Document $document)
|
||||
{
|
||||
// Clear current output
|
||||
$this->output = '';
|
||||
// Keep track of style modifications
|
||||
$this->previousStyle = null;
|
||||
// and create a stack of styles
|
||||
$this->styles = array();
|
||||
// Put an initial standard style onto the stack
|
||||
$this->style = new Style();
|
||||
$this->styles[] = $this->style;
|
||||
|
||||
// Keep track of opened html tags
|
||||
$this->openedTags = [
|
||||
'span' => false,
|
||||
'p' => false
|
||||
];
|
||||
// Create the first paragraph
|
||||
$this->openTag('p');
|
||||
// Begin format
|
||||
$this->processGroup($document->root);
|
||||
// Close opened tags
|
||||
$this->closeTags(true);
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
protected function processGroup(Group $group)
|
||||
{
|
||||
if ($group instanceof Groups\Destination) {
|
||||
$this->processDestination($group->children);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($group instanceof Groups\ColorTable) {
|
||||
Style::$colortbl = $group->getColors();
|
||||
return;
|
||||
}
|
||||
if ($group instanceof Groups\FontTable) {
|
||||
Style::$fonttbl = $group->getFonts();
|
||||
return;
|
||||
}
|
||||
if ($group instanceof Groups\Pict) {
|
||||
$this->output .= $group->getHTML();
|
||||
return;
|
||||
}
|
||||
if ($group instanceof Groups\StyleSheet) {
|
||||
// Stylesheet extraction not yet supported
|
||||
return;
|
||||
}
|
||||
|
||||
if ($group instanceof Groups\FileTable
|
||||
|| $group instanceof Groups\Info
|
||||
|| $group instanceof Groups\ListTable
|
||||
|| $group instanceof Groups\ListText
|
||||
|| $group instanceof Groups\ListOverrideTable
|
||||
) {
|
||||
// Skip
|
||||
return;
|
||||
}
|
||||
|
||||
// Special group processing:
|
||||
switch ($group->getType())
|
||||
{
|
||||
case 'nonshppict':
|
||||
// Ignore alternative images
|
||||
return;
|
||||
}
|
||||
|
||||
// Pictures extraction not yet supported
|
||||
//if (\substr($group->getType(), 0, 4) == "pict") return;
|
||||
|
||||
// Push a new style onto the stack:
|
||||
$this->style = clone $this->style;
|
||||
$this->styles[] = $this->style;
|
||||
|
||||
foreach ($group->children as $child) {
|
||||
$this->processEntity($child);
|
||||
}
|
||||
|
||||
// Pop style from stack
|
||||
\array_pop($this->styles);
|
||||
$this->style = \end($this->styles);
|
||||
}
|
||||
|
||||
protected function processDestination(array $dest)
|
||||
{
|
||||
// Check if this is a Word 97 picture
|
||||
if ($dest[0] instanceof ControlWord && 'shppict' == $dest[0]->word) {
|
||||
$c = \count($dest);
|
||||
for ($i = 1; $i < $c; ++$i) {
|
||||
$this->processEntity($dest[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function processEntity(Entity $entry)
|
||||
{
|
||||
if ($entry instanceof Group) $this->processGroup($entry);
|
||||
else if ($entry instanceof ControlWord) $this->processControlWord($entry);
|
||||
else if ($entry instanceof ControlSymbol) $this->processControlSymbol($entry);
|
||||
else if ($entry instanceof Text) $this->write($entry);
|
||||
}
|
||||
|
||||
protected function processControlWord(ControlWord $word)
|
||||
{
|
||||
switch ($word->word) {
|
||||
case 'plain': // Reset font formatting properties to default.
|
||||
case 'pard': // Reset to default paragraph properties.
|
||||
$this->style->reset($this->defaultFont);
|
||||
break;
|
||||
|
||||
/* Font formatting properties: */
|
||||
|
||||
case 'b': // Bold
|
||||
case 'caps': // All capitals
|
||||
case 'embo': // Emboss
|
||||
case 'i': // Italic
|
||||
case 'impr': // Engrave (imprint)
|
||||
case 'outl': // Outline
|
||||
case 'scaps': // Small capitals
|
||||
case 'shad': // Shadow
|
||||
case 'strike': // Strike-through
|
||||
case 'striked': // Double strike-through
|
||||
$this->style->{$word->word} = $word->parameter;
|
||||
break;
|
||||
case 'sub': // Subscripts
|
||||
case 'super': // Superscripts
|
||||
$this->style->verticalAlign = $word->word;
|
||||
break;
|
||||
case 'nosupersub': // Turns off superscripting or subscripting
|
||||
$this->style->verticalAlign = null;
|
||||
break;
|
||||
case 'ul': // Underline
|
||||
case 'uld': // Dotted underline
|
||||
case 'uldash': // Dashed underline
|
||||
case 'uldashd': // Dash-dotted underline
|
||||
case 'uldashdd': // Dash-dot-dotted underline
|
||||
case 'uldb': // Double underline
|
||||
case 'ulhwave': // Heavy wave underline
|
||||
case 'ulldash': // Long dashed underline
|
||||
case 'ulnone': // no underline
|
||||
case 'ulth': // Thick underline
|
||||
case 'ulthd': // Thick dotted underline
|
||||
case 'ulthdash': // Thick dashed underline
|
||||
case 'ulthdashd': // Thick dash-dotted underline
|
||||
case 'ulthdashdd': // Thick dash-dot-dotted underline
|
||||
case 'ulthldash': // Thick long dashed underline
|
||||
case 'ululdbwave': // Double wave underline
|
||||
case 'ulw': // Word underline
|
||||
case 'ulwave': // Wave underline
|
||||
$this->style->underline = $word->word;
|
||||
break;
|
||||
case 'v': // hidden
|
||||
$this->style->hidden = $word->parameter;
|
||||
break;
|
||||
case 'fs': // Font size in half-points
|
||||
$this->style->fs = $word->parameter;
|
||||
break;
|
||||
case 'f': // Font number
|
||||
$this->style->font = $word->parameter;
|
||||
break;
|
||||
case 'deff': // Store default font
|
||||
$this->defaultFont = $word->parameter;
|
||||
break;
|
||||
/*
|
||||
case 'ltrmark':
|
||||
$this->style->dir = 'ltr';
|
||||
break;
|
||||
case 'rtlmark':
|
||||
$this->style->dir = 'rtl';
|
||||
break;
|
||||
|
||||
\ab* Associated font is bold.
|
||||
\acaps* Associated font is all capitals.
|
||||
\acfN Associated foreground color (default is 0).
|
||||
\adnN Associated font is subscript position in half-points (default is 6).
|
||||
\aexpndN Expansion or compression of the space between characters in quarter-points; a negative value compresses (default is 0).
|
||||
\afN Associated font number (default is 0).
|
||||
\afsN Associated font size in half-points (default is 24).
|
||||
\ai* Associated font is italic.
|
||||
\alangN Language ID (see the standard language table) for the associated font.
|
||||
\aoutl* Associated font is outline.
|
||||
\ascaps* Associated font is small capitals.
|
||||
\ashad* Associated font is shadow.
|
||||
\astrike* Associated font is strikethrough.
|
||||
\aul Associated font is continuous underline. \aul0 turns off all underlining for the alternate font.
|
||||
\auld Associated font is dotted underline.
|
||||
\auldb Associated font is double underline.
|
||||
\aulnone Associated font is no longer underlined.
|
||||
\aulw Associated font is word underline.
|
||||
\aupN Superscript position in half-points (default is 6).
|
||||
\fcsN N = 1 means South East Asian complex script; N = 0 means not South East Asian script
|
||||
\loch The text consists of single-byte low-ANSI (0x00–0x7F) characters.
|
||||
\hich The text consists of single-byte high-ANSI (0x80–0xFF) characters.
|
||||
\dbch The text consists of double-byte characters.
|
||||
*/
|
||||
|
||||
/* Colors */
|
||||
|
||||
case 'cf':
|
||||
case 'chcfpat':
|
||||
$this->style->fontcolor = $word->parameter;
|
||||
break;
|
||||
case 'cb':
|
||||
case 'chcbpat':
|
||||
$this->style->background = $word->parameter;
|
||||
break;
|
||||
case 'highlight':
|
||||
case 'ulc': // Underline color
|
||||
$this->style->{$word->word} = $word->parameter;
|
||||
break;
|
||||
|
||||
/* Special characters */
|
||||
|
||||
case 'lquote': $this->write('‘'); break;
|
||||
case 'rquote': $this->write('’'); break;
|
||||
case 'ldblquote': $this->write('“'); break;
|
||||
case 'rdblquote': $this->write('”'); break;
|
||||
case 'bullet': $this->write('•'); break;
|
||||
case 'endash': $this->write('–'); break;
|
||||
case 'emdash': $this->write('—'); break;
|
||||
case 'enspace': $this->write(' '); break;
|
||||
case 'emspace': $this->write(' '); break;
|
||||
case 'qmspace': $this->write(' '); break;
|
||||
case 'tab': $this->write("\t"); break;
|
||||
case 'line': $this->output .= '<br/>'; break; // "\r\n"
|
||||
// case 'lbr': $this->output .= '<br/>'; break;
|
||||
case 'zwbo': $this->write("\xC2\x82"); break;
|
||||
case 'zwnbo': $this->write("\xC2\x83"); break;
|
||||
case 'zwj': $this->write("\xE2\x80\x8D"); break;
|
||||
case 'zwnj': $this->write("\xE2\x80\x8C"); break;
|
||||
|
||||
/* Unicode characters */
|
||||
|
||||
case 'u':
|
||||
$this->write($this->decodeUnicode($word->parameter));
|
||||
break;
|
||||
|
||||
/* Paragraphs */
|
||||
|
||||
case 'par': // End of paragraph
|
||||
case 'row': // End of table row
|
||||
// Close previously opened tags
|
||||
$this->closeTags();
|
||||
// Begin a new paragraph
|
||||
$this->openTag('p');
|
||||
break;
|
||||
|
||||
/* Tables */
|
||||
case 'cell': // End of table cell
|
||||
case 'nestcell': // End of nested table cell
|
||||
case 'nestrow': // End of nested table row
|
||||
break;
|
||||
|
||||
/* Code pages */
|
||||
case 'ansi':
|
||||
case 'mac':
|
||||
case 'pc':
|
||||
case 'pca':
|
||||
$this->RTFencoding = Encoding::getFromCodepage($word->word);
|
||||
break;
|
||||
case 'ansicpg':
|
||||
if ($word->parameter) {
|
||||
$this->RTFencoding = Encoding::getFromCodepage($word->parameter);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromtext': // Indicates document was originally plain text email.
|
||||
case 'fromhtml': // Indicates document was originally HTML email and may contain encapsulated HTML tags.
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function decodeUnicode($code, $srcEnc = 'UTF-8')
|
||||
{
|
||||
$utf8 = '';
|
||||
if ($srcEnc && 'UTF-8' != $srcEnc) {
|
||||
// convert character to Unicode
|
||||
$utf8 = \iconv($srcEnc, 'UTF-8', \chr($code));
|
||||
}
|
||||
return $utf8 ? $utf8 : \mb_convert_encoding("&#{$code};", 'UTF-8', 'HTML-ENTITIES');
|
||||
}
|
||||
|
||||
protected function write(string $txt)
|
||||
{
|
||||
//$tag = \str_replace('er', '', $this->style->verticalAlign ?: 'span'); // sub / sup / span
|
||||
// Create a new 'span' element only when a style change occurs.
|
||||
// 1st case: style change occured
|
||||
// 2nd case: there is no change in style but the already created 'span'
|
||||
// element is somehow closed (ex. because of an end of paragraph)
|
||||
if (!$this->style->equals($this->previousStyle) ||
|
||||
($this->style->equals($this->previousStyle) && !$this->openedTags['span'])
|
||||
) {
|
||||
$this->closeTag('span');
|
||||
$this->previousStyle = clone $this->style;
|
||||
$this->openTag('span', $this->style);
|
||||
}
|
||||
$this->output .= $txt;
|
||||
}
|
||||
|
||||
protected function openTag(string $tag, string $attr = '')
|
||||
{
|
||||
$this->output .= $attr ? "<{$tag} {$attr}>" : "<{$tag}>";
|
||||
$this->openedTags[$tag] = true;
|
||||
}
|
||||
|
||||
protected function closeTag(string $tag, bool $end = false)
|
||||
{
|
||||
if ($this->openedTags[$tag]) {
|
||||
// Check for empty html elements
|
||||
if (\substr($this->output , -\strlen("<{$tag}>")) == "<{$tag}>") {
|
||||
if ('p' === $tag && !$end) {
|
||||
// Replace empty 'p' element with a line break
|
||||
$this->output = \substr($this->output ,0, -3) . "<br>";
|
||||
} else {
|
||||
// Delete empty elements
|
||||
$this->output = \substr($this->output ,0, -\strlen("<{$tag}>"));
|
||||
}
|
||||
} else {
|
||||
$this->output .= "</{$tag}>";
|
||||
}
|
||||
$this->openedTags[$tag] = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function closeTags(bool $end = false)
|
||||
{
|
||||
foreach ($this->openedTags as $tag => $b) {
|
||||
$this->closeTag($tag, $end);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processControlSymbol(ControlSymbol $symbol)
|
||||
{
|
||||
if ("'" === $symbol->symbol) {
|
||||
// "\'hh" = A hexadecimal value, based on the specified character set
|
||||
$enc = $this->getSourceEncoding();
|
||||
$uchar = $this->decodeUnicode($symbol->parameter, $enc);
|
||||
$this->write($uchar);
|
||||
} else if ('~' === $symbol->symbol) {
|
||||
// Non breaking space
|
||||
// $this->write(' ');
|
||||
// $this->write("\xC2\xA0");
|
||||
$this->write(' ');
|
||||
} else if ('-' === $symbol->symbol) {
|
||||
// Optional hyphen
|
||||
// $this->write("­");
|
||||
// $this->write("\xC2\xAD");
|
||||
$this->write('');
|
||||
} else if ('_' === $symbol->symbol) {
|
||||
// Non breaking hyphen
|
||||
// $this->write("‑");
|
||||
// $this->write("\xE2\x80\x91");
|
||||
$this->write('‑');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getSourceEncoding()
|
||||
{
|
||||
if (isset($this->style->font)) {
|
||||
if (isset(Style::$fonttbl[$this->style->font]->codepage)) {
|
||||
return Style::$fonttbl[$this->style->font]->codepage;
|
||||
}
|
||||
if (isset(Style::$fonttbl[$this->style->font]->charset)) {
|
||||
return Style::$fonttbl[$this->style->font]->charset;
|
||||
}
|
||||
}
|
||||
return $this->RTFencoding;
|
||||
}
|
||||
|
||||
protected function ord_utf8($chr)
|
||||
{
|
||||
$ord0 = \ord($chr);
|
||||
if ($ord0 >= 0 && $ord0 <= 127)
|
||||
return $ord0;
|
||||
|
||||
$ord1 = \ord($chr[1]);
|
||||
if ($ord0 >= 192 && $ord0 <= 223)
|
||||
return ($ord0 - 192) * 64 + ($ord1 - 128);
|
||||
|
||||
$ord2 = \ord($chr[2]);
|
||||
if ($ord0 >= 224 && $ord0 <= 239)
|
||||
return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128);
|
||||
|
||||
$ord3 = \ord($chr[3]);
|
||||
if ($ord0 >= 240 && $ord0 <= 247)
|
||||
return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2 - 128) * 64 + ($ord3 - 128);
|
||||
|
||||
$ord4 = \ord($chr[4]);
|
||||
if ($ord0 >= 248 && $ord0 <= 251)
|
||||
return ($ord0 - 248) * 16777216 + ($ord1 - 128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128);
|
||||
|
||||
if ($ord0 >= 252 && $ord0 <= 253)
|
||||
return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (\ord($chr[5]) - 128);
|
||||
|
||||
\trigger_error("Invalid Unicode character: {$chr}");
|
||||
}
|
||||
}
|
||||
|
||||
class Style implements \Stringable
|
||||
{
|
||||
public static
|
||||
$fonttbl = [],
|
||||
$colortbl = [];
|
||||
|
||||
private static
|
||||
$highlights = [
|
||||
1 => 'Black',
|
||||
2 => 'Blue',
|
||||
3 => 'Cyan',
|
||||
4 => 'Green',
|
||||
5 => 'Magenta',
|
||||
6 => 'Red',
|
||||
7 => 'Yellow',
|
||||
8 => 'Unused',
|
||||
9 => 'DarkBlue',
|
||||
10 => 'DarkCyan',
|
||||
11 => 'DarkGreen',
|
||||
12 => 'DarkMagenta',
|
||||
13 => 'DarkRed',
|
||||
14 => 'DarkYellow',
|
||||
15 => 'DarkGray',
|
||||
16 => 'LightGray'
|
||||
],
|
||||
$underlineStyles = [
|
||||
'ul' => 'solid 1px', // Underline
|
||||
'uld' => 'dotted 1px', // Dotted underline
|
||||
'uldash' => 'dashed 1px', // Dashed underline
|
||||
'uldashd' => 'dashed 1px', // Dash-dotted underline
|
||||
'uldashdd' => 'dashed 1px', // Dash-dot-dotted underline
|
||||
'uldb' => 'double 1px', // Double underline
|
||||
'ulhwave' => 'wavy 3px', // Heavy wave underline
|
||||
'ulldash' => 'dashed 1px', // Long dashed underline
|
||||
'ulnone' => '', // no underline
|
||||
'ulth' => 'solid 2px', // Thick underline
|
||||
'ulthd' => 'dotted 2px', // Thick dotted underline
|
||||
'ulthdash' => 'dashed 2px', // Thick dashed underline
|
||||
'ulthdashd' => 'dashed 2px', // Thick dash-dotted underline
|
||||
'ulthdashdd' => 'dashed 2px', // Thick dash-dot-dotted underline
|
||||
'ulthldash' => 'dashed 2px', // Thick long dashed underline
|
||||
'ululdbwave' => 'wavy 2px', // Double wave underline
|
||||
// 'ulw' => 'wavy 1px', // Word underline
|
||||
'ulwave' => 'wavy 1px', // Wave underline
|
||||
];
|
||||
|
||||
public bool
|
||||
$b = false, // Bold
|
||||
$caps = false, // All capitals
|
||||
$embo = false, // Emboss
|
||||
$i = false, // Italic
|
||||
$impr = false, // Engrave (imprint)
|
||||
$outl = false, // Outline
|
||||
$scaps = false, // Small capitals
|
||||
$shad = false, // Shadow
|
||||
$strike = false, // Strike-through
|
||||
$striked = false, // Double strike-through
|
||||
$hidden = false;
|
||||
|
||||
public int
|
||||
$fs = 0; // Font size in half-points, default is 24 = 12pt
|
||||
|
||||
public
|
||||
$underline = 'ulnone',
|
||||
$verticalAlign = '', // sub / super
|
||||
$fontcolor = null,
|
||||
$background = null,
|
||||
$font = null,
|
||||
$highlight = null,
|
||||
$ulc = null; // Underline color
|
||||
|
||||
public function reset($defaultFont = null) : void
|
||||
{
|
||||
$this->b = false;
|
||||
$this->caps = false;
|
||||
$this->embo = false;
|
||||
$this->i = false;
|
||||
$this->impr = false;
|
||||
$this->outl = false;
|
||||
$this->scaps = false;
|
||||
$this->shad = false;
|
||||
$this->strike = false;
|
||||
$this->striked = false;
|
||||
$this->hidden = false;
|
||||
$this->fs = 0;
|
||||
$this->underline = 'ulnone';
|
||||
$this->verticalAlign = '';
|
||||
$this->fontcolor = null;
|
||||
$this->background = null;
|
||||
$this->highlight = null;
|
||||
$this->ulc = null;
|
||||
$this->font = isset($defaultFont) ? $defaultFont : null;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$style = [];
|
||||
|
||||
if ($this->b ) $style[] = 'font-weight:bold';
|
||||
if ($this->caps ) $style[] = 'text-transform:uppercase';
|
||||
// if ($this->embo ) $style[] = 'text-shadow: 1px 4px 4px #555;background-clip:text';
|
||||
if ($this->i ) $style[] = 'font-style:italic';
|
||||
// if ($this->impr ) $style[] = 'text-shadow: rgba(245,245,245,0.5) 3px 5px 1px;background-clip:text';
|
||||
// if ($this->outl ) $style[] = '';
|
||||
if ($this->scaps ) $style[] = 'font-variant:small-caps';
|
||||
// if ($this->shad ) $style[] = '';
|
||||
if ($this->strike ) $style[] = 'text-decoration:line-through';
|
||||
// if ($this->striked ) $style[] = '';
|
||||
if ($this->verticalAlign) $style[] = "vertical-align:{$this->verticalAlign};font-size:smaller";
|
||||
if (!empty(static::$underlineStyles[$this->underline])) {
|
||||
$value = 'text-decoration:underline';
|
||||
if (!empty(static::$colortbl[$this->ulc])) {
|
||||
$value .= ' ' . static::$colortbl[$this->ulc];
|
||||
}
|
||||
$value .= ' ' . static::$underlineStyles[$this->underline];
|
||||
$style[] = $value;
|
||||
}
|
||||
if ($this->hidden) $style[] = 'display:none';
|
||||
if (!empty(static::$fonttbl[$this->font])) {
|
||||
$font = (string) static::$fonttbl[$this->font];
|
||||
if ($font) {
|
||||
$style[] = $font;
|
||||
}
|
||||
}
|
||||
if (0 < $this->fs) {
|
||||
// $style[] = 'font-size:'.\ceil($this->fs / 2).'pt';
|
||||
// $style[] = 'font-size:'.\ceil($word->parameter * 16 / 24).'px';
|
||||
$style[] = 'font-size:'.\round($this->fs / 24, 2).'em';
|
||||
}
|
||||
if (!empty(static::$colortbl[$this->fontcolor])) {
|
||||
$style[] = 'color:' . static::$colortbl[$this->fontcolor];
|
||||
}
|
||||
if (!empty(static::$colortbl[$this->background])) {
|
||||
$style[] = 'background-color:' . static::$colortbl[$this->background];
|
||||
} else if (!empty(static::$highlights[$this->highlight])) {
|
||||
$style[] = 'background-color:' . static::$highlights[$this->highlight];
|
||||
}
|
||||
return $style ? 'style="'.\implode(';', $style).'"' : '';
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether this Style is equal to another Style.
|
||||
*/
|
||||
public function equals(?Style $style) : bool
|
||||
{
|
||||
return $style
|
||||
&& ($this->b == $style->b)
|
||||
&& ($this->caps == $style->caps)
|
||||
// && ($this->embo == $style->embo)
|
||||
&& ($this->i == $style->i)
|
||||
// && ($this->impr == $style->impr)
|
||||
// && ($this->outl == $style->outl)
|
||||
&& ($this->scaps == $style->scaps)
|
||||
// && ($this->shad == $style->shad)
|
||||
&& ($this->strike == $style->strike)
|
||||
&& ($this->striked == $style->striked)
|
||||
&& ($this->verticalAlign == $style->verticalAlign)
|
||||
&& ($this->underline == $style->underline)
|
||||
&& ($this->ulc == $style->ulc)
|
||||
&& ($this->hidden == $style->hidden)
|
||||
&& ($this->fs == $style->fs)
|
||||
&& ($this->fontcolor == $style->fontcolor)
|
||||
&& ($this->background == $style->background)
|
||||
&& ($this->highlight == $style->highlight)
|
||||
&& ($this->font == $style->font)
|
||||
;
|
||||
}
|
||||
}
|
18
snappymail/v/0.0.0/app/libraries/snappymail/rtf/text.php
Normal file
18
snappymail/v/0.0.0/app/libraries/snappymail/rtf/text.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail\Rtf;
|
||||
|
||||
class Text implements Entity, \Stringable
|
||||
{
|
||||
public string $text;
|
||||
|
||||
public function __construct(string $text)
|
||||
{
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue