mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 12:52:42 +01:00
Rewrite Aphlict to use Websockets
Summary: Fixes T6559. No more flash, use Websockets. This is less aggressive than the earlier version, and retains more server logic. - Support "wss". - Make the client work. - Remove "notification.user" entirely. - Seems ok? Test Plan: In Safari, Firefox and Chrome, saw the browsers connect. Made a bunch of comments/updates and saw notifications. Notable holes in the test plan: - Haven't tested "wss" yet. I'll do this on secure. - Notifications are //too fast// now, locally. I get them after I hit submit but before the page reloads. - There are probably some other rough edges, this is a fairly big patch. Reviewers: joshuaspence, btrahan Reviewed By: joshuaspence, btrahan Subscribers: fabe, btrahan, epriestley Maniphest Tasks: T6713, T6559 Differential Revision: https://secure.phabricator.com/D11143
This commit is contained in:
parent
9a8eb4026e
commit
9e0f70e17d
31 changed files with 394 additions and 2776 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -30,3 +30,6 @@
|
||||||
|
|
||||||
# User extensions
|
# User extensions
|
||||||
/src/extensions/*
|
/src/extensions/*
|
||||||
|
|
||||||
|
# NPM local packages
|
||||||
|
/support/aphlict/server/node_modules/
|
||||||
|
|
581
externals/vegas/LICENSE
vendored
581
externals/vegas/LICENSE
vendored
|
@ -1,581 +0,0 @@
|
||||||
Mozilla Public License 1.1 (MPL 1.1)
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
1.0.1. "Commercial Use" means distribution or
|
|
||||||
otherwise making the Covered Code available to a third
|
|
||||||
party.
|
|
||||||
|
|
||||||
1.1. ''Contributor'' means each entity that creates or
|
|
||||||
contributes to the creation of Modifications.
|
|
||||||
|
|
||||||
1.2. ''Contributor Version'' means the combination of
|
|
||||||
the Original Code, prior Modifications used by
|
|
||||||
a Contributor, and the Modifications made by that
|
|
||||||
particular Contributor.
|
|
||||||
|
|
||||||
1.3. ''Covered Code'' means the Original Code or
|
|
||||||
Modifications or the combination of the Original Code
|
|
||||||
and Modifications, in each case including portions
|
|
||||||
thereof.
|
|
||||||
|
|
||||||
1.4. ''Electronic Distribution Mechanism'' means a
|
|
||||||
mechanism generally accepted in the software
|
|
||||||
development community for the electronic transfer of
|
|
||||||
data.
|
|
||||||
|
|
||||||
1.5. ''Executable'' means Covered Code in any form
|
|
||||||
other than Source Code.
|
|
||||||
|
|
||||||
1.6. ''Initial Developer'' means the individual or
|
|
||||||
entity identified as the Initial Developer in the
|
|
||||||
Source Code notice required by Exhibit A.
|
|
||||||
|
|
||||||
1.7. ''Larger Work'' means a work which combines
|
|
||||||
Covered Code or portions thereof with code not
|
|
||||||
governed by the terms of this License.
|
|
||||||
|
|
||||||
1.8. ''License'' means this document.
|
|
||||||
|
|
||||||
1.8.1. "Licensable" means having the right to grant,
|
|
||||||
to the maximum extent possible, whether at the time of
|
|
||||||
the initial grant or subsequently acquired, any and
|
|
||||||
all of the rights conveyed herein.
|
|
||||||
|
|
||||||
1.9. ''Modifications'' means any addition to or
|
|
||||||
deletion from the substance or structure of either the
|
|
||||||
Original Code or any previous Modifications. When
|
|
||||||
Covered Code is released as a series of files, a
|
|
||||||
Modification is:
|
|
||||||
|
|
||||||
A. Any addition to or deletion from the contents
|
|
||||||
of a file containing Original Code or previous
|
|
||||||
Modifications.
|
|
||||||
|
|
||||||
B. Any new file that contains any part of the
|
|
||||||
Original Code or previous Modifications.
|
|
||||||
|
|
||||||
1.10. ''Original Code'' means Source Code of computer
|
|
||||||
software code which is described in the Source Code
|
|
||||||
notice required by Exhibit A as Original Code, and
|
|
||||||
which, at the time of its release under this License
|
|
||||||
is not already Covered Code governed by this License.
|
|
||||||
|
|
||||||
1.10.1. "Patent Claims" means any patent claim(s), now
|
|
||||||
owned or hereafter acquired, including without
|
|
||||||
limitation, method, process, and apparatus claims, in
|
|
||||||
any patent Licensable by grantor.
|
|
||||||
|
|
||||||
1.11. ''Source Code'' means the preferred form of the
|
|
||||||
Covered Code for making modifications to it, including
|
|
||||||
all modules it contains, plus any associated interface
|
|
||||||
definition files, scripts used to control compilation
|
|
||||||
and installation of an Executable, or source code
|
|
||||||
differential comparisons against either the Original
|
|
||||||
Code or another well known, available Covered Code of
|
|
||||||
the Contributor's choice. The Source Code can be in a
|
|
||||||
compressed or archival form, provided the appropriate
|
|
||||||
decompression or de-archiving software is widely
|
|
||||||
available for no charge.
|
|
||||||
|
|
||||||
1.12. "You'' (or "Your") means an individual or a
|
|
||||||
legal entity exercising rights under, and complying
|
|
||||||
with all of the terms of, this License or a future
|
|
||||||
version of this License issued under Section 6.1. For
|
|
||||||
legal entities, "You'' includes any entity which
|
|
||||||
controls, is controlled by, or is under common control
|
|
||||||
with You. For purposes of this definition, "control''
|
|
||||||
means (a) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by
|
|
||||||
contract or otherwise, or (b) ownership of more than
|
|
||||||
fifty percent (50%) of the outstanding shares or
|
|
||||||
beneficial ownership of such entity.
|
|
||||||
|
|
||||||
|
|
||||||
2. Source Code License.
|
|
||||||
|
|
||||||
2.1. The Initial Developer Grant.
|
|
||||||
The Initial Developer hereby grants You a world-wide,
|
|
||||||
royalty-free, non-exclusive license, subject to third
|
|
||||||
party intellectual property claims:
|
|
||||||
(a) under intellectual property rights (other
|
|
||||||
than patent or trademark) Licensable by Initial
|
|
||||||
Developer to use, reproduce, modify, display,
|
|
||||||
perform, sublicense and distribute the Original
|
|
||||||
Code (or portions thereof) with or without
|
|
||||||
Modifications, and/or as part of a Larger Work;
|
|
||||||
and
|
|
||||||
|
|
||||||
(b) under Patents Claims infringed by the making,
|
|
||||||
using or selling of Original Code, to make, have
|
|
||||||
made, use, practice, sell, and offer for sale,
|
|
||||||
and/or otherwise dispose of the Original Code (or
|
|
||||||
portions thereof).
|
|
||||||
|
|
||||||
(c) the licenses granted in this Section 2.1(a)
|
|
||||||
and (b) are effective on the date Initial
|
|
||||||
Developer first distributes Original Code under
|
|
||||||
the terms of this License.
|
|
||||||
|
|
||||||
(d) Notwithstanding Section 2.1(b) above, no
|
|
||||||
patent license is granted: 1) for code that You
|
|
||||||
delete from the Original Code; 2) separate from
|
|
||||||
the Original Code; or 3) for infringements
|
|
||||||
caused by: i) the modification of the Original
|
|
||||||
Code or ii) the combination of the Original Code
|
|
||||||
with other software or devices.
|
|
||||||
|
|
||||||
2.2. Contributor Grant.
|
|
||||||
Subject to third party intellectual property claims,
|
|
||||||
each Contributor hereby grants You a world-wide,
|
|
||||||
royalty-free, non-exclusive license
|
|
||||||
|
|
||||||
(a) under intellectual property rights (other
|
|
||||||
than patent or trademark) Licensable by
|
|
||||||
Contributor, to use, reproduce, modify, display,
|
|
||||||
perform, sublicense and distribute the
|
|
||||||
Modifications created by such Contributor (or
|
|
||||||
portions thereof) either on an unmodified basis,
|
|
||||||
with other Modifications, as Covered Code and/or
|
|
||||||
as part of a Larger Work;
|
|
||||||
and
|
|
||||||
|
|
||||||
(b) under Patent Claims infringed by the making,
|
|
||||||
using, or selling of Modifications made by that
|
|
||||||
Contributor either alone and/or in combination
|
|
||||||
with its Contributor Version (or portions of such
|
|
||||||
combination), to make, use, sell, offer for sale,
|
|
||||||
have made, and/or otherwise dispose of: 1)
|
|
||||||
Modifications made by that Contributor (or
|
|
||||||
portions thereof); and 2) the combination of
|
|
||||||
Modifications made by that Contributor with its
|
|
||||||
Contributor Version (or portions of such
|
|
||||||
combination).
|
|
||||||
|
|
||||||
(c) the licenses granted in Sections 2.2(a) and
|
|
||||||
2.2(b) are effective on the date Contributor
|
|
||||||
first makes Commercial Use of the Covered Code.
|
|
||||||
|
|
||||||
(d) Notwithstanding Section 2.2(b) above, no
|
|
||||||
patent license is granted: 1) for any code that
|
|
||||||
Contributor has deleted from the Contributor
|
|
||||||
Version; 2) separate from the Contributor
|
|
||||||
Version; 3) for infringements caused by: i)
|
|
||||||
third party modifications of Contributor Version
|
|
||||||
or ii) the combination of Modifications made by
|
|
||||||
that Contributor with other software (except as
|
|
||||||
part of the Contributor Version) or other
|
|
||||||
devices; or 4) under Patent Claims infringed by
|
|
||||||
Covered Code in the absence of Modifications made
|
|
||||||
by that Contributor.
|
|
||||||
|
|
||||||
|
|
||||||
3. Distribution Obligations.
|
|
||||||
|
|
||||||
3.1. Application of License.
|
|
||||||
The Modifications which You create or to which You
|
|
||||||
contribute are governed by the terms of this License,
|
|
||||||
including without limitation Section 2.2. The Source
|
|
||||||
Code version of Covered Code may be distributed only
|
|
||||||
under the terms of this License or a future version of
|
|
||||||
this License released under Section 6.1, and You must
|
|
||||||
include a copy of this License with every copy of the
|
|
||||||
Source Code You distribute. You may not offer or
|
|
||||||
impose any terms on any Source Code version that
|
|
||||||
alters or restricts the applicable version of this
|
|
||||||
License or the recipients' rights hereunder. However,
|
|
||||||
You may include an additional document offering the
|
|
||||||
additional rights described in Section 3.5.
|
|
||||||
|
|
||||||
3.2. Availability of Source Code.
|
|
||||||
Any Modification which You create or to which You
|
|
||||||
contribute must be made available in Source Code form
|
|
||||||
under the terms of this License either on the same
|
|
||||||
media as an Executable version or via an accepted
|
|
||||||
Electronic Distribution Mechanism to anyone to whom
|
|
||||||
you made an Executable version available; and if made
|
|
||||||
available via Electronic Distribution Mechanism, must
|
|
||||||
remain available for at least twelve (12) months after
|
|
||||||
the date it initially became available, or at least
|
|
||||||
six (6) months after a subsequent version of that
|
|
||||||
particular Modification has been made available to
|
|
||||||
such recipients. You are responsible for ensuring that
|
|
||||||
the Source Code version remains available even if the
|
|
||||||
Electronic Distribution Mechanism is maintained by a
|
|
||||||
third party.
|
|
||||||
|
|
||||||
3.3. Description of Modifications.
|
|
||||||
You must cause all Covered Code to which You
|
|
||||||
contribute to contain a file documenting the changes
|
|
||||||
You made to create that Covered Code and the date of
|
|
||||||
any change. You must include a prominent statement
|
|
||||||
that the Modification is derived, directly or
|
|
||||||
indirectly, from Original Code provided by the Initial
|
|
||||||
Developer and including the name of the Initial
|
|
||||||
Developer in (a) the Source Code, and (b) in any
|
|
||||||
notice in an Executable version or related
|
|
||||||
documentation in which You describe the origin or
|
|
||||||
ownership of the Covered Code.
|
|
||||||
|
|
||||||
3.4. Intellectual Property Matters
|
|
||||||
(a) Third Party Claims.
|
|
||||||
If Contributor has knowledge that a license under
|
|
||||||
a third party's intellectual property rights is
|
|
||||||
required to exercise the rights granted by such
|
|
||||||
Contributor under Sections 2.1 or 2.2,
|
|
||||||
Contributor must include a text file with the
|
|
||||||
Source Code distribution titled "LEGAL'' which
|
|
||||||
describes the claim and the party making the
|
|
||||||
claim in sufficient detail that a recipient will
|
|
||||||
know whom to contact. If Contributor obtains such
|
|
||||||
knowledge after the Modification is made
|
|
||||||
available as described in Section 3.2,
|
|
||||||
Contributor shall promptly modify the LEGAL file
|
|
||||||
in all copies Contributor makes available
|
|
||||||
thereafter and shall take other steps (such as
|
|
||||||
notifying appropriate mailing lists or
|
|
||||||
newsgroups) reasonably calculated to inform those
|
|
||||||
who received the Covered Code that new knowledge
|
|
||||||
has been obtained.
|
|
||||||
|
|
||||||
(b) Contributor APIs.
|
|
||||||
If Contributor's Modifications include an
|
|
||||||
application programming interface and Contributor
|
|
||||||
has knowledge of patent licenses which are
|
|
||||||
reasonably necessary to implement that API,
|
|
||||||
Contributor must also include this information in
|
|
||||||
the LEGAL file.
|
|
||||||
|
|
||||||
(c) Representations.
|
|
||||||
Contributor represents that, except as disclosed
|
|
||||||
pursuant to Section 3.4(a) above, Contributor
|
|
||||||
believes that Contributor's Modifications are
|
|
||||||
Contributor's original creation(s) and/or
|
|
||||||
Contributor has sufficient rights to grant the
|
|
||||||
rights conveyed by this License.
|
|
||||||
|
|
||||||
3.5. Required Notices.
|
|
||||||
You must duplicate the notice in Exhibit A in each
|
|
||||||
file of the Source Code. If it is not possible to put
|
|
||||||
such notice in a particular Source Code file due to
|
|
||||||
its structure, then You must include such notice in a
|
|
||||||
location (such as a relevant directory) where a user
|
|
||||||
would be likely to look for such a notice. If You
|
|
||||||
created one or more Modification(s) You may add your
|
|
||||||
name as a Contributor to the notice described in
|
|
||||||
Exhibit A. You must also duplicate this License in
|
|
||||||
any documentation for the Source Code where You
|
|
||||||
describe recipients' rights or ownership rights
|
|
||||||
relating to Covered Code. You may choose to offer,
|
|
||||||
and to charge a fee for, warranty, support, indemnity
|
|
||||||
or liability obligations to one or more recipients of
|
|
||||||
Covered Code. However, You may do so only on Your own
|
|
||||||
behalf, and not on behalf of the Initial Developer or
|
|
||||||
any Contributor. You must make it absolutely clear
|
|
||||||
than any such warranty, support, indemnity or
|
|
||||||
liability obligation is offered by You alone, and You
|
|
||||||
hereby agree to indemnify the Initial Developer and
|
|
||||||
every Contributor for any liability incurred by the
|
|
||||||
Initial Developer or such Contributor as a result of
|
|
||||||
warranty, support, indemnity or liability terms You
|
|
||||||
offer.
|
|
||||||
|
|
||||||
3.6. Distribution of Executable Versions.
|
|
||||||
You may distribute Covered Code in Executable form
|
|
||||||
only if the requirements of Section 3.1-3.5 have been
|
|
||||||
met for that Covered Code, and if You include a notice
|
|
||||||
stating that the Source Code version of the Covered
|
|
||||||
Code is available under the terms of this License,
|
|
||||||
including a description of how and where You have
|
|
||||||
fulfilled the obligations of Section 3.2. The notice
|
|
||||||
must be conspicuously included in any notice in an
|
|
||||||
Executable version, related documentation or
|
|
||||||
collateral in which You describe recipients' rights
|
|
||||||
relating to the Covered Code. You may distribute the
|
|
||||||
Executable version of Covered Code or ownership rights
|
|
||||||
under a license of Your choice, which may contain
|
|
||||||
terms different from this License, provided that You
|
|
||||||
are in compliance with the terms of this License and
|
|
||||||
that the license for the Executable version does not
|
|
||||||
attempt to limit or alter the recipient's rights in
|
|
||||||
the Source Code version from the rights set forth in
|
|
||||||
this License. If You distribute the Executable version
|
|
||||||
under a different license You must make it absolutely
|
|
||||||
clear that any terms which differ from this License
|
|
||||||
are offered by You alone, not by the Initial Developer
|
|
||||||
or any Contributor. You hereby agree to indemnify the
|
|
||||||
Initial Developer and every Contributor for any
|
|
||||||
liability incurred by the Initial Developer or such
|
|
||||||
Contributor as a result of any such terms You offer.
|
|
||||||
|
|
||||||
3.7. Larger Works.
|
|
||||||
You may create a Larger Work by combining Covered Code
|
|
||||||
with other code not governed by the terms of this
|
|
||||||
License and distribute the Larger Work as a single
|
|
||||||
product. In such a case, You must make sure the
|
|
||||||
requirements of this License are fulfilled for the
|
|
||||||
Covered Code.
|
|
||||||
|
|
||||||
|
|
||||||
4. Inability to Comply Due to Statute or Regulation.
|
|
||||||
|
|
||||||
If it is impossible for You to comply with any of the
|
|
||||||
terms of this License with respect to some or all of
|
|
||||||
the Covered Code due to statute, judicial order, or
|
|
||||||
regulation then You must: (a) comply with the terms of
|
|
||||||
this License to the maximum extent possible; and (b)
|
|
||||||
describe the limitations and the code they affect.
|
|
||||||
Such description must be included in the LEGAL file
|
|
||||||
described in Section 3.4 and must be included with all
|
|
||||||
distributions of the Source Code. Except to the extent
|
|
||||||
prohibited by statute or regulation, such description
|
|
||||||
must be sufficiently detailed for a recipient of
|
|
||||||
ordinary skill to be able to understand it.
|
|
||||||
|
|
||||||
|
|
||||||
5. Application of this License.
|
|
||||||
|
|
||||||
This License applies to code to which the Initial
|
|
||||||
Developer has attached the notice in Exhibit A and to
|
|
||||||
related Covered Code.
|
|
||||||
|
|
||||||
|
|
||||||
6. Versions of the License.
|
|
||||||
|
|
||||||
6.1. New Versions.
|
|
||||||
Netscape Communications Corporation (''Netscape'') may
|
|
||||||
publish revised and/or new versions of the License
|
|
||||||
from time to time. Each version will be given a
|
|
||||||
distinguishing version number.
|
|
||||||
|
|
||||||
6.2. Effect of New Versions.
|
|
||||||
Once Covered Code has been published under a
|
|
||||||
particular version of the License, You may always
|
|
||||||
continue to use it under the terms of that version.
|
|
||||||
You may also choose to use such Covered Code under the
|
|
||||||
terms of any subsequent version of the License
|
|
||||||
published by Netscape. No one other than Netscape has
|
|
||||||
the right to modify the terms applicable to Covered
|
|
||||||
Code created under this License.
|
|
||||||
|
|
||||||
6.3. Derivative Works.
|
|
||||||
If You create or use a modified version of this
|
|
||||||
License (which you may only do in order to apply it to
|
|
||||||
code which is not already Covered Code governed by
|
|
||||||
this License), You must (a) rename Your license so
|
|
||||||
that the phrases ''Mozilla'', ''MOZILLAPL'',
|
|
||||||
''MOZPL'', ''Netscape'', "MPL", ''NPL'' or any
|
|
||||||
confusingly similar phrase do not appear in your
|
|
||||||
license (except to note that your license differs from
|
|
||||||
this License) and (b) otherwise make it clear that
|
|
||||||
Your version of the license contains terms which
|
|
||||||
differ from the Mozilla Public License and Netscape
|
|
||||||
Public License. (Filling in the name of the Initial
|
|
||||||
Developer, Original Code or Contributor in the notice
|
|
||||||
described in Exhibit A shall not of themselves be
|
|
||||||
deemed to be modifications of this License.)
|
|
||||||
|
|
||||||
|
|
||||||
7. DISCLAIMER OF WARRANTY.
|
|
||||||
|
|
||||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS
|
|
||||||
IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
||||||
EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
|
|
||||||
WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS,
|
|
||||||
MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-
|
|
||||||
INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
|
|
||||||
PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD
|
|
||||||
ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU
|
|
||||||
(NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR)
|
|
||||||
ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR
|
|
||||||
CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN
|
|
||||||
ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED
|
|
||||||
CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
|
|
||||||
DISCLAIMER.
|
|
||||||
|
|
||||||
|
|
||||||
8. TERMINATION.
|
|
||||||
|
|
||||||
8.1. This License and the rights granted hereunder
|
|
||||||
will terminate automatically if You fail to comply
|
|
||||||
with terms herein and fail to cure such breach within
|
|
||||||
30 days of becoming aware of the breach. All
|
|
||||||
sublicenses to the Covered Code which are properly
|
|
||||||
granted shall survive any termination of this License.
|
|
||||||
Provisions which, by their nature, must remain in
|
|
||||||
effect beyond the termination of this License shall
|
|
||||||
survive.
|
|
||||||
|
|
||||||
8.2. If You initiate litigation by asserting a patent
|
|
||||||
infringement claim (excluding declatory judgment
|
|
||||||
actions) against Initial Developer or a Contributor
|
|
||||||
(the Initial Developer or Contributor against whom You
|
|
||||||
file such action is referred to as "Participant")
|
|
||||||
alleging that:
|
|
||||||
|
|
||||||
(a) such Participant's Contributor Version directly
|
|
||||||
or indirectly infringes any patent, then any and all
|
|
||||||
rights granted by such Participant to You under
|
|
||||||
Sections 2.1 and/or 2.2 of this License shall, upon 60
|
|
||||||
days notice from Participant terminate prospectively,
|
|
||||||
unless if within 60 days after receipt of notice You
|
|
||||||
either: (i) agree in writing to pay Participant a
|
|
||||||
mutually agreeable reasonable royalty for Your past
|
|
||||||
and future use of Modifications made by such
|
|
||||||
Participant, or (ii) withdraw Your litigation claim
|
|
||||||
with respect to the Contributor Version against such
|
|
||||||
Participant. If within 60 days of notice, a
|
|
||||||
reasonable royalty and payment arrangement are not
|
|
||||||
mutually agreed upon in writing by the parties or the
|
|
||||||
litigation claim is not withdrawn, the rights granted
|
|
||||||
by Participant to You under Sections 2.1 and/or 2.2
|
|
||||||
automatically terminate at the expiration of the 60
|
|
||||||
day notice period specified above.
|
|
||||||
|
|
||||||
(b) any software, hardware, or device, other than
|
|
||||||
such Participant's Contributor Version, directly or
|
|
||||||
indirectly infringes any patent, then any rights
|
|
||||||
granted to You by such Participant under Sections 2.1
|
|
||||||
(b) and 2.2(b) are revoked effective as of the date
|
|
||||||
You first made, used, sold, distributed, or had made,
|
|
||||||
Modifications made by that Participant.
|
|
||||||
|
|
||||||
8.3. If You assert a patent infringement claim
|
|
||||||
against Participant alleging that such Participant's
|
|
||||||
Contributor Version directly or indirectly infringes
|
|
||||||
any patent where such claim is resolved (such as by
|
|
||||||
license or settlement) prior to the initiation of
|
|
||||||
patent infringement litigation, then the reasonable
|
|
||||||
value of the licenses granted by such Participant
|
|
||||||
under Sections 2.1 or 2.2 shall be taken into account
|
|
||||||
in determining the amount or value of any payment or
|
|
||||||
license.
|
|
||||||
|
|
||||||
8.4. In the event of termination under Sections 8.1
|
|
||||||
or 8.2 above, all end user license agreements
|
|
||||||
(excluding distributors and resellers) which have been
|
|
||||||
validly granted by You or any distributor hereunder
|
|
||||||
prior to termination shall survive termination.
|
|
||||||
|
|
||||||
|
|
||||||
9. LIMITATION OF LIABILITY.
|
|
||||||
|
|
||||||
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
|
|
||||||
WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR
|
|
||||||
OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER
|
|
||||||
CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR
|
|
||||||
ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY
|
|
||||||
PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
|
|
||||||
CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING,
|
|
||||||
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK
|
|
||||||
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND
|
|
||||||
ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH
|
|
||||||
PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT
|
|
||||||
APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
|
|
||||||
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
|
|
||||||
APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME
|
|
||||||
JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION
|
|
||||||
OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS
|
|
||||||
EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
|
|
||||||
10. U.S. GOVERNMENT END USERS.
|
|
||||||
|
|
||||||
The Covered Code is a ''commercial item,'' as that
|
|
||||||
term is defined in 48 C.F.R. 2.101 (Oct. 1995),
|
|
||||||
consisting of ''commercial computer software'' and
|
|
||||||
''commercial computer software documentation,'' as
|
|
||||||
such terms are used in 48 C.F.R. 12.212 (Sept. 1995).
|
|
||||||
Consistent with 48 C.F.R. 12.212 and 48 C.F.R.
|
|
||||||
227.7202-1 through 227.7202-4 (June 1995), all U.S.
|
|
||||||
Government End Users acquire Covered Code with only
|
|
||||||
those rights set forth herein.
|
|
||||||
|
|
||||||
|
|
||||||
11. MISCELLANEOUS.
|
|
||||||
|
|
||||||
This License represents the complete agreement
|
|
||||||
concerning subject matter hereof. If any provision of
|
|
||||||
this License is held to be unenforceable, such
|
|
||||||
provision shall be reformed only to the extent
|
|
||||||
necessary to make it enforceable. This License shall
|
|
||||||
be governed by California law provisions (except to
|
|
||||||
the extent applicable law, if any, provides
|
|
||||||
otherwise), excluding its conflict-of-law provisions.
|
|
||||||
With respect to disputes in which at least one party
|
|
||||||
is a citizen of, or an entity chartered or registered
|
|
||||||
to do business in the United States of America, any
|
|
||||||
litigation relating to this License shall be subject
|
|
||||||
to the jurisdiction of the Federal Courts of the
|
|
||||||
Northern District of California, with venue lying in
|
|
||||||
Santa Clara County, California, with the losing party
|
|
||||||
responsible for costs, including without limitation,
|
|
||||||
court costs and reasonable attorneys' fees and
|
|
||||||
expenses. The application of the United Nations
|
|
||||||
Convention on Contracts for the International Sale of
|
|
||||||
Goods is expressly excluded. Any law or regulation
|
|
||||||
which provides that the language of a contract shall
|
|
||||||
be construed against the drafter shall not apply to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
|
|
||||||
12. RESPONSIBILITY FOR CLAIMS.
|
|
||||||
|
|
||||||
As between Initial Developer and the Contributors,
|
|
||||||
each party is responsible for claims and damages
|
|
||||||
arising, directly or indirectly, out of its
|
|
||||||
utilization of rights under this License and You agree
|
|
||||||
to work with Initial Developer and Contributors to
|
|
||||||
distribute such responsibility on an equitable basis.
|
|
||||||
Nothing herein is intended or shall be deemed to
|
|
||||||
constitute any admission of liability.
|
|
||||||
|
|
||||||
|
|
||||||
13. MULTIPLE-LICENSED CODE.
|
|
||||||
|
|
||||||
Initial Developer may designate portions of the
|
|
||||||
Covered Code as Multiple-Licensed. Multiple-Licensed
|
|
||||||
means that the Initial Developer permits you to
|
|
||||||
utilize portions of the Covered Code under Your choice
|
|
||||||
of the NPL or the alternative licenses, if any,
|
|
||||||
specified by the Initial Developer in the file
|
|
||||||
described in Exhibit A.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EXHIBIT A - Mozilla Public License.
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the License.
|
|
||||||
|
|
||||||
The Original Code is ______________________________________.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is
|
|
||||||
________________________.
|
|
||||||
Portions created by ______________________ are Copyright (C) ______
|
|
||||||
_______________________. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s): ______________________________________.
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
the _____ license (the [___] License), in which case the provisions of
|
|
||||||
[______] License are applicable instead of those above. If you wish to
|
|
||||||
allow use of your version of this file only under the terms of the [____]
|
|
||||||
License and not to allow others to use your version of this file under
|
|
||||||
the MPL, indicate your decision by deleting the provisions above and
|
|
||||||
replace them with the notice and other provisions required by the [___]
|
|
||||||
License. If you do not delete the provisions above, a recipient may use
|
|
||||||
your version of this file under either the MPL or the [___] License.
|
|
||||||
|
|
||||||
[NOTE: The text of this Exhibit A may differ slightly from the text of
|
|
||||||
the notices in the Source Code files of the Original Code. You should use
|
|
||||||
the text of this Exhibit A rather than the text found in the Original Code
|
|
||||||
Source Code for Your Modifications.]
|
|
||||||
|
|
29
externals/vegas/README
vendored
29
externals/vegas/README
vendored
|
@ -1,29 +0,0 @@
|
||||||
VEGAS AS3 - version 1.8.5.2228
|
|
||||||
|
|
||||||
The "vegas.swc" library contains all the AS3 source code of the VEGAS project with all this extensions.
|
|
||||||
|
|
||||||
LICENCE
|
|
||||||
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
PROJECT PAGES
|
|
||||||
|
|
||||||
* http://code.google.com/p/vegas/
|
|
||||||
|
|
||||||
DOCUMENTATION & CO
|
|
||||||
|
|
||||||
* http://code.google.com/p/vegas/ (tutorials and install)
|
|
||||||
* http://code.google.com/p/vegas/issues/list (issues)
|
|
||||||
* http://www.ekameleon.net/vegas/docs
|
|
||||||
|
|
||||||
* http://www.ekameleon.net/blog/ (french blog)
|
|
||||||
|
|
||||||
ABOUT AUTHOR
|
|
||||||
|
|
||||||
* Author : ALCARAZ Marc (eKameleon)
|
|
||||||
* Link : http://www.ekameleon.net/blog
|
|
||||||
* Mail : ekameleon@gmail.com
|
|
||||||
|
|
||||||
NOTES
|
|
||||||
|
|
||||||
The vegas.swc file target now the FlashPlayer 10.2 and build with the Flex SDK 4.5.0.19786.
|
|
86
externals/vegas/src/system/Serializer.as
vendored
86
externals/vegas/src/system/Serializer.as
vendored
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the
|
|
||||||
License.
|
|
||||||
|
|
||||||
The Original Code is [maashaack framework].
|
|
||||||
|
|
||||||
The Initial Developers of the Original Code are
|
|
||||||
Zwetan Kjukov <zwetan@gmail.com> and Marc Alcaraz <ekameleon@gmail.com>.
|
|
||||||
Portions created by the Initial Developers are Copyright (C) 2006-2011
|
|
||||||
the Initial Developers. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s):
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of the MPL, indicate your
|
|
||||||
decision by deleting the provisions above and replace them with the notice
|
|
||||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package system
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines what a Serializer have to implements to be integrated in the framework.
|
|
||||||
* <p><b>Note :</b> Every serializers (eden, json, wddx, etc.) should implement it.</p>
|
|
||||||
*/
|
|
||||||
public interface Serializer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The prettyIndent value of the serializer.
|
|
||||||
*/
|
|
||||||
function get prettyIndent():int;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function set prettyIndent( value:int ):void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The prettyPrinting value of the serializer.
|
|
||||||
*/
|
|
||||||
function get prettyPrinting():Boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function set prettyPrinting( value:Boolean ):void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The identor String value of the serializer.
|
|
||||||
*/
|
|
||||||
function get indentor():String;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function set indentor( value:String ):void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize the specified String source representation.
|
|
||||||
*/
|
|
||||||
function deserialize( source:String ):*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize the specified object.
|
|
||||||
*/
|
|
||||||
function serialize( value:* ):String;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
107
externals/vegas/src/vegas/strings/JSON.as
vendored
107
externals/vegas/src/vegas/strings/JSON.as
vendored
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the License.
|
|
||||||
|
|
||||||
The Original Code is VEGAS Framework.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is
|
|
||||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
|
||||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
|
||||||
the Initial Developer. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s) :
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of the MPL, indicate your
|
|
||||||
decision by deleting the provisions above and replace them with the notice
|
|
||||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package vegas.strings
|
|
||||||
{
|
|
||||||
import vegas.strings.json.JSONSerializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code class="prettyprint">JSON</code> (JavaScript object Notation) is a lightweight data-interchange format.
|
|
||||||
* <p>More information in the official site : <a href="http://www.JSON.org/">http://www.JSON.org</a></p>
|
|
||||||
* <p>Add Hexa Digits tool in deserialize method - <a href="http://code.google.com/p/edenrr/">eden inspiration</a></p>
|
|
||||||
*
|
|
||||||
* @example Example
|
|
||||||
* <listing version="3.0">
|
|
||||||
* <code class="prettyprint">
|
|
||||||
* import core.getClassName ;
|
|
||||||
*
|
|
||||||
* import vegas.strings.JSON;
|
|
||||||
* import vegas.strings.errors.JSONError;
|
|
||||||
*
|
|
||||||
* // --- Init
|
|
||||||
*
|
|
||||||
* var a:Array = [2, true, "hello"] ;
|
|
||||||
* var o:Object = { prop1 : 1 , prop2 : 2 } ;
|
|
||||||
* var s:String = "hello world" ;
|
|
||||||
* var n:Number = 4 ;
|
|
||||||
* var b:Boolean = true ;
|
|
||||||
*
|
|
||||||
* trace("# Serialize \r") ;
|
|
||||||
*
|
|
||||||
* trace("- a : " + JSON.serialize( a ) ) ;
|
|
||||||
* trace("- o : " + JSON.serialize( o ) ) ;
|
|
||||||
* trace("- s : " + JSON.serialize( s ) ) ;
|
|
||||||
* trace("- n : " + JSON.serialize( n ) ) ;
|
|
||||||
* trace("- b : " + JSON.serialize( b ) ) ;
|
|
||||||
*
|
|
||||||
* trace ("\r# Deserialize \r") ;
|
|
||||||
*
|
|
||||||
* var source:String = '[ { "prop1" : 0xFF0000 , prop2:2, prop3:"hello", prop4:true} , 2, true, 3, [3, 2] ]' ;
|
|
||||||
*
|
|
||||||
* o = JSON.deserialize(source) ;
|
|
||||||
*
|
|
||||||
* var l:uint = o.length ;
|
|
||||||
* for (var i:uint = 0 ; i < l ; i++)
|
|
||||||
* {
|
|
||||||
* trace("> " + i + " : " + o[i] + " -> typeof :: " + typeof(o[i])) ;
|
|
||||||
* if (typeof(o[i]) == "object")
|
|
||||||
* {
|
|
||||||
* for (var each:String in o[i])
|
|
||||||
* {
|
|
||||||
* trace(" > " + each + " : " + o[i][each] + " :: " + getClassName(o[i][each]) ) ;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* trace ("\r# JSONError \r") ;
|
|
||||||
*
|
|
||||||
* source = "[3, 2," ; // test1
|
|
||||||
*
|
|
||||||
* // var source:String = '{"prop1":coucou"}' ; // test2
|
|
||||||
*
|
|
||||||
* try
|
|
||||||
* {
|
|
||||||
* var errorObj:Object = JSON.deserialize(source) ;
|
|
||||||
* }
|
|
||||||
* catch( e:JSONError )
|
|
||||||
* {
|
|
||||||
* trace( e.toString() ) ;
|
|
||||||
* }
|
|
||||||
* </code>
|
|
||||||
* </listing>
|
|
||||||
*/
|
|
||||||
public var JSON:JSONSerializer = new JSONSerializer() ;
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the License.
|
|
||||||
|
|
||||||
The Original Code is VEGAS Framework.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is
|
|
||||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
|
||||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
|
||||||
the Initial Developer. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s) :
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of the MPL, indicate your
|
|
||||||
decision by deleting the provisions above and replace them with the notice
|
|
||||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package vegas.strings.json
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* This JSONError is throw in the JSON static methods.
|
|
||||||
*/
|
|
||||||
public class JSONError extends Error
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a new JSONError instance.
|
|
||||||
*/
|
|
||||||
public function JSONError( message:String, at:uint, source:String , id:int=0 )
|
|
||||||
{
|
|
||||||
super( message , id );
|
|
||||||
name = "JSONError" ;
|
|
||||||
this.at = at ;
|
|
||||||
this.source = source ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The position of char with an error parsing in the JSON String representation.
|
|
||||||
*/
|
|
||||||
public var at:uint ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source ot the bad parsing.
|
|
||||||
*/
|
|
||||||
public var source:String ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a String representation of the object.
|
|
||||||
* @return a String representation of the object.
|
|
||||||
*/
|
|
||||||
public function toString():String
|
|
||||||
{
|
|
||||||
var msg:String = "## " + name + " : " + message + " ##" ;
|
|
||||||
if (!isNaN(at))
|
|
||||||
{
|
|
||||||
msg += ", at:" + at ;
|
|
||||||
}
|
|
||||||
if ( source != null )
|
|
||||||
{
|
|
||||||
msg += " in \"" + source + "\"";
|
|
||||||
}
|
|
||||||
return msg ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,757 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the License.
|
|
||||||
|
|
||||||
The Original Code is VEGAS Framework.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is
|
|
||||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
|
||||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
|
||||||
the Initial Developer. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s) :
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of the MPL, indicate your
|
|
||||||
decision by deleting the provisions above and replace them with the notice
|
|
||||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package vegas.strings.json
|
|
||||||
{
|
|
||||||
import system.Serializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is the concrete class of the JSON singleton.
|
|
||||||
* <code class="prettyprint">JSON</code> (JavaScript object Notation) is a lightweight data-interchange format.
|
|
||||||
* <p>More information in the official site : <a href="http://www.JSON.org/">http://www.JSON.org</a></p>
|
|
||||||
* <p>Add Hexa Digits tool in deserialize method - <a href="http://code.google.com/p/edenrr/">eden inspiration</a></p>
|
|
||||||
* <p><b>Example :</b></p>
|
|
||||||
* <pre class="prettyprint">
|
|
||||||
* import core.getClassName ;
|
|
||||||
*
|
|
||||||
* import vegas.strings.JSON;
|
|
||||||
* import vegas.strings.JSONError;
|
|
||||||
*
|
|
||||||
* // --- Init
|
|
||||||
*
|
|
||||||
* var a:Array = [2, true, "hello"] ;
|
|
||||||
* var o:Object = { prop1 : 1 , prop2 : 2 } ;
|
|
||||||
* var s:String = "hello world" ;
|
|
||||||
* var n:Number = 4 ;
|
|
||||||
* var b:Boolean = true ;
|
|
||||||
*
|
|
||||||
* trace("Serialize") ;
|
|
||||||
*
|
|
||||||
* trace("- a : " + JSON.serialize( a ) ) ;
|
|
||||||
* trace("- o : " + JSON.serialize( o ) ) ;
|
|
||||||
* trace("- s : " + JSON.serialize( s ) ) ;
|
|
||||||
* trace("- n : " + JSON.serialize( n ) ) ;
|
|
||||||
* trace("- b : " + JSON.serialize( b ) ) ;
|
|
||||||
*
|
|
||||||
* trace ("Deserialize") ;
|
|
||||||
*
|
|
||||||
* var source:String = '[ { "prop1" : 0xFF0000 , prop2:2, prop3:"hello", prop4:true} , 2, true, 3, [3, 2] ]' ;
|
|
||||||
*
|
|
||||||
* o = JSON.deserialize(source) ;
|
|
||||||
*
|
|
||||||
* var l:uint = o.length ;
|
|
||||||
* for (var i:uint = 0 ; i < l ; i++)
|
|
||||||
* {
|
|
||||||
* trace("- " + i + " : " + o[i] + " , typeof :: " + typeof(o[i])) ;
|
|
||||||
* if (typeof(o[i]) == "object")
|
|
||||||
* {
|
|
||||||
* for (var each:String in o[i])
|
|
||||||
* {
|
|
||||||
* trace(" + " + each + " : " + o[i][each] + " :: " + getClassName(o[i][each]) ) ;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* trace ("JSONError") ;
|
|
||||||
*
|
|
||||||
* source = "[3, 2," ; // test1
|
|
||||||
*
|
|
||||||
* // var source:String = '{"prop1":coucou"}' ; // test2
|
|
||||||
*
|
|
||||||
* try
|
|
||||||
* {
|
|
||||||
* var errorObj:Object = JSON.deserialize(source) ;
|
|
||||||
* }
|
|
||||||
* catch( e:JSONError )
|
|
||||||
* {
|
|
||||||
* trace( e.toString() ) ;
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public class JSONSerializer implements Serializer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a new JSONSerializer instance.
|
|
||||||
*/
|
|
||||||
public function JSONSerializer()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source to evaluate.
|
|
||||||
*/
|
|
||||||
public var source:String ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the indentor string representation.
|
|
||||||
*/
|
|
||||||
public function get indentor():String
|
|
||||||
{
|
|
||||||
return _indentor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
public function set indentor(value:String):void
|
|
||||||
{
|
|
||||||
_indentor = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the pretty indent value.
|
|
||||||
*/
|
|
||||||
public function get prettyIndent():int
|
|
||||||
{
|
|
||||||
return _prettyIndent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
public function set prettyIndent(value:int):void
|
|
||||||
{
|
|
||||||
_prettyIndent = value ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the pretty printing flag value.
|
|
||||||
*/
|
|
||||||
public function get prettyPrinting():Boolean
|
|
||||||
{
|
|
||||||
return _prettyPrinting ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
public function set prettyPrinting(value:Boolean):void
|
|
||||||
{
|
|
||||||
_prettyPrinting = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a string and interpret the source code to the correct object construct.
|
|
||||||
* <p><b>Example :</b></p>
|
|
||||||
* <pre class="prettyprint">
|
|
||||||
* "hello world" --> "hello world"
|
|
||||||
* "0xFF" --> 255
|
|
||||||
* "{a:1,"b":2}" --> {a:1,b:2}
|
|
||||||
* </pre>
|
|
||||||
* @return a string representing the data.
|
|
||||||
*/
|
|
||||||
public function deserialize( source:String ):*
|
|
||||||
{
|
|
||||||
this.source = source ;
|
|
||||||
at = 0 ;
|
|
||||||
ch = ' ' ;
|
|
||||||
return value() ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize the specified value object passed-in argument.
|
|
||||||
*/
|
|
||||||
public function serialize( value:* ):String
|
|
||||||
{
|
|
||||||
var c:String ; // char
|
|
||||||
var i:int ;
|
|
||||||
var l:int ;
|
|
||||||
var s:String = '' ;
|
|
||||||
var v:* ;
|
|
||||||
var tof:String = typeof(value) ;
|
|
||||||
switch (tof)
|
|
||||||
{
|
|
||||||
case 'object' :
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
if (value is Array)
|
|
||||||
{
|
|
||||||
l = (value as Array).length ;
|
|
||||||
for (i = 0 ; i < l ; ++i)
|
|
||||||
{
|
|
||||||
v = serialize(value[i]);
|
|
||||||
if (s) s += ',' ;
|
|
||||||
s += v ;
|
|
||||||
}
|
|
||||||
return '[' + s + ']';
|
|
||||||
}
|
|
||||||
else if ( typeof( value.toString ) != 'undefined')
|
|
||||||
{
|
|
||||||
for (var prop:String in value)
|
|
||||||
{
|
|
||||||
v = value[prop];
|
|
||||||
if ( (typeof(v) != 'undefined') && (typeof(v) != 'function') )
|
|
||||||
{
|
|
||||||
v = serialize(v);
|
|
||||||
if (s)
|
|
||||||
{
|
|
||||||
s += ',' ;
|
|
||||||
}
|
|
||||||
s += serialize(prop) + ':' + v ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "{" + s + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
case 'number':
|
|
||||||
{
|
|
||||||
return isFinite(value) ? String(value) : 'null' ;
|
|
||||||
}
|
|
||||||
case 'string' :
|
|
||||||
{
|
|
||||||
l = (value as String).length ;
|
|
||||||
s = '"' ;
|
|
||||||
for (i = 0 ; i < l ; i += 1)
|
|
||||||
{
|
|
||||||
c = (value as String).charAt(i) ;
|
|
||||||
if (c >= ' ')
|
|
||||||
{
|
|
||||||
if (c == '\\' || c == '"')
|
|
||||||
{
|
|
||||||
s += '\\';
|
|
||||||
}
|
|
||||||
s += c;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\b':
|
|
||||||
{
|
|
||||||
s += '\\b';
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case '\f' :
|
|
||||||
{
|
|
||||||
s += '\\f' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case '\n' :
|
|
||||||
{
|
|
||||||
s += '\\n' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case '\r':
|
|
||||||
{
|
|
||||||
s += '\\r' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case '\t':
|
|
||||||
{
|
|
||||||
s += '\\t' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
var code:Number = c.charCodeAt() ;
|
|
||||||
s += '\\u00' + String(Math.floor(code / 16).toString(16)) + ((code % 16).toString(16)) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s + '"' ;
|
|
||||||
}
|
|
||||||
case 'boolean' :
|
|
||||||
{
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current position of the iterator in the source.
|
|
||||||
*/
|
|
||||||
protected var at:Number = 0 ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current character of the iterator in the source.
|
|
||||||
*/
|
|
||||||
protected var ch:String = ' ' ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the Array objects in the source expression.
|
|
||||||
*/
|
|
||||||
protected function array():Array
|
|
||||||
{
|
|
||||||
var a:Array = [];
|
|
||||||
if ( ch == '[' )
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
white() ;
|
|
||||||
if (ch == ']')
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
while (ch)
|
|
||||||
{
|
|
||||||
a.push( value() ) ;
|
|
||||||
white();
|
|
||||||
if (ch == ']')
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
else if (ch != ',')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
white();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error( JSONStrings.badArray );
|
|
||||||
return null ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws a JSONError with the passed-in message.
|
|
||||||
*/
|
|
||||||
protected function error( m:String ):void
|
|
||||||
{
|
|
||||||
throw new JSONError( m, at - 1 , source) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the passed-in character is a digit.
|
|
||||||
*/
|
|
||||||
protected function isDigit( c:String ):Boolean
|
|
||||||
{
|
|
||||||
return( ("0" <= c) && (c <= "9") );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the passed-in character is a hexadecimal digit.
|
|
||||||
*/
|
|
||||||
protected function isHexDigit( c:String ):Boolean
|
|
||||||
{
|
|
||||||
return( isDigit( c ) || (("A" <= c) && (c <= "F")) || (("a" <= c) && (c <= "f")) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the current character is a key.
|
|
||||||
*/
|
|
||||||
protected function key():*
|
|
||||||
{
|
|
||||||
var s:String = ch ;
|
|
||||||
var semiColon:int = source.indexOf( ':' , at ) ;
|
|
||||||
var quoteIndex:int = source.indexOf( '"' , at ) ;
|
|
||||||
var squoteIndex:int = source.indexOf( "'" , at ) ;
|
|
||||||
if( (quoteIndex <= semiColon && quoteIndex > -1) || (squoteIndex <= semiColon && squoteIndex > -1))
|
|
||||||
{
|
|
||||||
s = string() ;
|
|
||||||
white() ;
|
|
||||||
if(ch == ':')
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error(JSONStrings.badKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( next() ) // Use key handling
|
|
||||||
{
|
|
||||||
if (ch == ':')
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
if(ch <= ' ')
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s += ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error( JSONStrings.badKey ) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next character in the source String representation.
|
|
||||||
* @return the next character in the source String representation.
|
|
||||||
*/
|
|
||||||
protected function next():String
|
|
||||||
{
|
|
||||||
ch = source.charAt(at);
|
|
||||||
at += 1;
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the Number values in the source expression.
|
|
||||||
*/
|
|
||||||
protected function number():*
|
|
||||||
{
|
|
||||||
|
|
||||||
var n:* = '' ;
|
|
||||||
var v:* ;
|
|
||||||
var hex:String = '' ;
|
|
||||||
var sign:String = '' ;
|
|
||||||
if (ch == '-')
|
|
||||||
{
|
|
||||||
n = '-';
|
|
||||||
sign = n ;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
if( ch == "0" )
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
if( ( ch == "x") || ( ch == "X") )
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
while( isHexDigit( ch ) )
|
|
||||||
{
|
|
||||||
hex += ch ;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
if( hex == "" )
|
|
||||||
{
|
|
||||||
error(JSONStrings.malFormedHexadecimal) ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Number( sign + "0x" + hex ) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
n += "0" ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( isDigit(ch) )
|
|
||||||
{
|
|
||||||
n += ch ;
|
|
||||||
next() ;
|
|
||||||
}
|
|
||||||
if (ch == '.')
|
|
||||||
{
|
|
||||||
n += '.';
|
|
||||||
while (next() && ch >= '0' && ch <= '9')
|
|
||||||
{
|
|
||||||
n += ch ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = 1 * n ;
|
|
||||||
if (!isFinite(v))
|
|
||||||
{
|
|
||||||
error( JSONStrings.badNumber );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return v ;
|
|
||||||
}
|
|
||||||
return NaN ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the Object values in the source expression.
|
|
||||||
*/
|
|
||||||
protected function object():*
|
|
||||||
{
|
|
||||||
var k:* = {} ;
|
|
||||||
var o:* = {} ;
|
|
||||||
if (ch == '{')
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
white();
|
|
||||||
if (ch == '}')
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
return o ;
|
|
||||||
}
|
|
||||||
while (ch)
|
|
||||||
{
|
|
||||||
k = key() ;
|
|
||||||
white();
|
|
||||||
if (ch != ':')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
o[k] = value() ;
|
|
||||||
white();
|
|
||||||
if (ch == '}')
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
else if (ch != ',')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
white();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error( JSONStrings.badObject ) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the string objects in the source expression.
|
|
||||||
*/
|
|
||||||
protected function string():*
|
|
||||||
{
|
|
||||||
var i:* = '' ;
|
|
||||||
var s:* = '' ;
|
|
||||||
var t:* ;
|
|
||||||
var u:* ;
|
|
||||||
var outer:Boolean ;
|
|
||||||
if (ch == '"' || ch == "'" )
|
|
||||||
{
|
|
||||||
var outerChar:String = ch ;
|
|
||||||
while ( next() )
|
|
||||||
{
|
|
||||||
if (ch == outerChar)
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
return s ;
|
|
||||||
}
|
|
||||||
else if (ch == '\\')
|
|
||||||
{
|
|
||||||
switch ( next() )
|
|
||||||
{
|
|
||||||
case 'b':
|
|
||||||
{
|
|
||||||
s += '\b' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case 'f' :
|
|
||||||
{
|
|
||||||
s += '\f';
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case 'n':
|
|
||||||
{
|
|
||||||
s += '\n';
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case 'r' :
|
|
||||||
{
|
|
||||||
s += '\r';
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case 't' :
|
|
||||||
{
|
|
||||||
s += '\t' ;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
case 'u' :
|
|
||||||
{
|
|
||||||
u = 0;
|
|
||||||
for (i = 0; i < 4; i += 1)
|
|
||||||
{
|
|
||||||
t = parseInt( next() , 16 ) ;
|
|
||||||
if (!isFinite(t))
|
|
||||||
{
|
|
||||||
outer = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
u = u * 16 + t;
|
|
||||||
}
|
|
||||||
if(outer)
|
|
||||||
{
|
|
||||||
outer = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
s += String.fromCharCode(u);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
s += ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s += ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error( JSONStrings.badString );
|
|
||||||
return null ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluates the values in the source expression.
|
|
||||||
*/
|
|
||||||
protected function value():*
|
|
||||||
{
|
|
||||||
white() ;
|
|
||||||
if (ch == '{' )
|
|
||||||
{
|
|
||||||
return object();
|
|
||||||
}
|
|
||||||
else if ( ch == '[' )
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
else if ( ch == '"' || ch == "'" )
|
|
||||||
{
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
else if ( ch == '-' )
|
|
||||||
{
|
|
||||||
return number();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ( ch >= '0' && ch <= '9' ) ? number() : word() ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check all white spaces.
|
|
||||||
*/
|
|
||||||
protected function white():void
|
|
||||||
{
|
|
||||||
while (ch)
|
|
||||||
{
|
|
||||||
if (ch <= ' ')
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else if (ch == '/')
|
|
||||||
{
|
|
||||||
switch ( next() )
|
|
||||||
{
|
|
||||||
case '/' :
|
|
||||||
{
|
|
||||||
while ( next() && ch != '\n' && ch != '\r')
|
|
||||||
{
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '*' :
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (ch)
|
|
||||||
{
|
|
||||||
if (ch == '*')
|
|
||||||
{
|
|
||||||
if ( next() == '/' )
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error( JSONStrings.unterminatedComment );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
error( JSONStrings.syntaxError );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check all special words in the source to evaluate.
|
|
||||||
*/
|
|
||||||
protected function word():*
|
|
||||||
{
|
|
||||||
if (ch == 't')
|
|
||||||
{
|
|
||||||
if (next() == 'r' && next() == 'u' && next() == 'e')
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( ch == 'f' )
|
|
||||||
{
|
|
||||||
if (next() == 'a' && next() == 'l' && next() == 's' && next() == 'e')
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
return false ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( ch == 'n' )
|
|
||||||
{
|
|
||||||
if (next() == 'u' && next() == 'l' && next() == 'l')
|
|
||||||
{
|
|
||||||
next() ;
|
|
||||||
return null ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error( JSONStrings.syntaxError );
|
|
||||||
return null ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private var _prettyIndent:int = 0 ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private var _prettyPrinting:Boolean ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private var _indentor:String = " " ;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
|
|
||||||
The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
for the specific language governing rights and limitations under the License.
|
|
||||||
|
|
||||||
The Original Code is VEGAS Framework.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is
|
|
||||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
|
||||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
|
||||||
the Initial Developer. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s) :
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of the MPL, indicate your
|
|
||||||
decision by deleting the provisions above and replace them with the notice
|
|
||||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package vegas.strings.json
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The string messages used in the JSON class.
|
|
||||||
*/
|
|
||||||
public class JSONStrings
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The bad Array error message.
|
|
||||||
*/
|
|
||||||
public static var badArray:String = "Bad Array" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bad key error message.
|
|
||||||
*/
|
|
||||||
public static var badKey:String = "Bad key" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bad Number error message.
|
|
||||||
*/
|
|
||||||
public static var badNumber:String = "Bad Number" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bad Object error message.
|
|
||||||
*/
|
|
||||||
public static var badObject:String = "Bad Object" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bad String error message.
|
|
||||||
*/
|
|
||||||
public static var badString:String = "Bad String" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mal formed Hexadecimal error message.
|
|
||||||
*/
|
|
||||||
public static var malFormedHexadecimal:String = "Mal formed Hexadecimal" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The syntax error message.
|
|
||||||
*/
|
|
||||||
public static var syntaxError:String = "Syntax Error" ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unterminated comment error message.
|
|
||||||
*/
|
|
||||||
public static var unterminatedComment:String = "Unterminated Comment" ;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => '63e782fb',
|
'core.pkg.css' => '8fc8031a',
|
||||||
'core.pkg.js' => '44aac665',
|
'core.pkg.js' => '21041609',
|
||||||
'darkconsole.pkg.js' => '8ab24e01',
|
'darkconsole.pkg.js' => '8ab24e01',
|
||||||
'differential.pkg.css' => '8af45893',
|
'differential.pkg.css' => '8af45893',
|
||||||
'differential.pkg.js' => 'dad3622f',
|
'differential.pkg.js' => 'dad3622f',
|
||||||
|
@ -342,9 +342,9 @@ return array(
|
||||||
'rsrc/image/texture/table_header.png' => '5c433037',
|
'rsrc/image/texture/table_header.png' => '5c433037',
|
||||||
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
|
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
|
||||||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||||
'rsrc/js/application/aphlict/Aphlict.js' => '4a07e8e3',
|
'rsrc/js/application/aphlict/Aphlict.js' => '464d333a',
|
||||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'f6bc26f0',
|
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'f6bc26f0',
|
||||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'a826c925',
|
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '1162a152',
|
||||||
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '58f7803f',
|
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '58f7803f',
|
||||||
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
||||||
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
|
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
|
||||||
|
@ -489,7 +489,6 @@ return array(
|
||||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||||
'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4',
|
'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4',
|
||||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||||
'rsrc/swf/aphlict.swf' => 'f19daffb',
|
|
||||||
),
|
),
|
||||||
'symbols' => array(
|
'symbols' => array(
|
||||||
'almanac-css' => 'dbb9b3af',
|
'almanac-css' => 'dbb9b3af',
|
||||||
|
@ -536,10 +535,10 @@ return array(
|
||||||
'herald-rule-editor' => '335fd41f',
|
'herald-rule-editor' => '335fd41f',
|
||||||
'herald-test-css' => '778b008e',
|
'herald-test-css' => '778b008e',
|
||||||
'inline-comment-summary-css' => '8cfd34e8',
|
'inline-comment-summary-css' => '8cfd34e8',
|
||||||
'javelin-aphlict' => '4a07e8e3',
|
'javelin-aphlict' => '464d333a',
|
||||||
'javelin-behavior' => '61cbc29a',
|
'javelin-behavior' => '61cbc29a',
|
||||||
'javelin-behavior-aphlict-dropdown' => 'f6bc26f0',
|
'javelin-behavior-aphlict-dropdown' => 'f6bc26f0',
|
||||||
'javelin-behavior-aphlict-listen' => 'a826c925',
|
'javelin-behavior-aphlict-listen' => '1162a152',
|
||||||
'javelin-behavior-aphlict-status' => '58f7803f',
|
'javelin-behavior-aphlict-status' => '58f7803f',
|
||||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||||
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
|
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
|
||||||
|
@ -909,6 +908,18 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
'1162a152' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-aphlict',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-request',
|
||||||
|
'javelin-uri',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-json',
|
||||||
|
'javelin-router',
|
||||||
|
'javelin-util',
|
||||||
|
'phabricator-notification',
|
||||||
|
),
|
||||||
'13c739ea' => array(
|
'13c739ea' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1079,6 +1090,13 @@ return array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'464d333a' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-websocket',
|
||||||
|
'javelin-leader',
|
||||||
|
'javelin-json',
|
||||||
|
),
|
||||||
'469c0d9e' => array(
|
'469c0d9e' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1100,10 +1118,6 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
'4a07e8e3' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-util',
|
|
||||||
),
|
|
||||||
'4d94d9c3' => array(
|
'4d94d9c3' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1489,18 +1503,6 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'a826c925' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-aphlict',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-request',
|
|
||||||
'javelin-uri',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-json',
|
|
||||||
'javelin-router',
|
|
||||||
'javelin-util',
|
|
||||||
'phabricator-notification',
|
|
||||||
),
|
|
||||||
'a8d8459d' => array(
|
'a8d8459d' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -2024,6 +2026,11 @@ return array(
|
||||||
'sprite-tokens-css',
|
'sprite-tokens-css',
|
||||||
'tokens-css',
|
'tokens-css',
|
||||||
'phui-status-list-view-css',
|
'phui-status-list-view-css',
|
||||||
|
'phui-feed-story-css',
|
||||||
|
'phabricator-feed-css',
|
||||||
|
'phabricator-dashboard-css',
|
||||||
|
'aphront-multi-column-view-css',
|
||||||
|
'phui-action-header-view-css',
|
||||||
),
|
),
|
||||||
'core.pkg.js' => array(
|
'core.pkg.js' => array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -2093,6 +2100,11 @@ return array(
|
||||||
'javelin-behavior-phabricator-show-older-transactions',
|
'javelin-behavior-phabricator-show-older-transactions',
|
||||||
'javelin-behavior-phui-timeline-dropdown-menu',
|
'javelin-behavior-phui-timeline-dropdown-menu',
|
||||||
'javelin-behavior-doorkeeper-tag',
|
'javelin-behavior-doorkeeper-tag',
|
||||||
|
'phabricator-title',
|
||||||
|
'javelin-leader',
|
||||||
|
'javelin-websocket',
|
||||||
|
'javelin-behavior-dashboard-async-panel',
|
||||||
|
'javelin-behavior-dashboard-tab-panel',
|
||||||
),
|
),
|
||||||
'darkconsole.pkg.js' => array(
|
'darkconsole.pkg.js' => array(
|
||||||
'javelin-behavior-dark-console',
|
'javelin-behavior-dark-console',
|
||||||
|
|
|
@ -69,6 +69,11 @@ return array(
|
||||||
'javelin-behavior-phabricator-show-older-transactions',
|
'javelin-behavior-phabricator-show-older-transactions',
|
||||||
'javelin-behavior-phui-timeline-dropdown-menu',
|
'javelin-behavior-phui-timeline-dropdown-menu',
|
||||||
'javelin-behavior-doorkeeper-tag',
|
'javelin-behavior-doorkeeper-tag',
|
||||||
|
'phabricator-title',
|
||||||
|
'javelin-leader',
|
||||||
|
'javelin-websocket',
|
||||||
|
'javelin-behavior-dashboard-async-panel',
|
||||||
|
'javelin-behavior-dashboard-tab-panel',
|
||||||
),
|
),
|
||||||
'core.pkg.css' => array(
|
'core.pkg.css' => array(
|
||||||
'phabricator-core-css',
|
'phabricator-core-css',
|
||||||
|
@ -126,6 +131,12 @@ return array(
|
||||||
'sprite-tokens-css',
|
'sprite-tokens-css',
|
||||||
'tokens-css',
|
'tokens-css',
|
||||||
'phui-status-list-view-css',
|
'phui-status-list-view-css',
|
||||||
|
|
||||||
|
'phui-feed-story-css',
|
||||||
|
'phabricator-feed-css',
|
||||||
|
'phabricator-dashboard-css',
|
||||||
|
'aphront-multi-column-view-css',
|
||||||
|
'phui-action-header-view-css',
|
||||||
),
|
),
|
||||||
'differential.pkg.css' => array(
|
'differential.pkg.css' => array(
|
||||||
'differential-core-view-css',
|
'differential-core-view-css',
|
||||||
|
|
|
@ -1239,7 +1239,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
|
'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
|
||||||
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
||||||
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
||||||
'PhabricatorAphlictManagementBuildWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementBuildWorkflow.php',
|
|
||||||
'PhabricatorAphlictManagementDebugWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php',
|
'PhabricatorAphlictManagementDebugWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php',
|
||||||
'PhabricatorAphlictManagementRestartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php',
|
'PhabricatorAphlictManagementRestartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php',
|
||||||
'PhabricatorAphlictManagementStartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php',
|
'PhabricatorAphlictManagementStartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php',
|
||||||
|
@ -4393,7 +4392,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
|
'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||||
'PhabricatorAnchorView' => 'AphrontView',
|
'PhabricatorAnchorView' => 'AphrontView',
|
||||||
'PhabricatorAphlictManagementBuildWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
|
||||||
'PhabricatorAphlictManagementDebugWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
'PhabricatorAphlictManagementDebugWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||||
'PhabricatorAphlictManagementRestartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
'PhabricatorAphlictManagementRestartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||||
'PhabricatorAphlictManagementStartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
'PhabricatorAphlictManagementStartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorAphlictManagementBuildWorkflow
|
|
||||||
extends PhabricatorAphlictManagementWorkflow {
|
|
||||||
|
|
||||||
public function didConstruct() {
|
|
||||||
$this
|
|
||||||
->setName('build')
|
|
||||||
->setSynopsis(pht('Build the Aphlict client.'))
|
|
||||||
->setArguments(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'debug',
|
|
||||||
'help' => 'Enable a debug build.',
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$root = dirname(__FILE__).'/../../../..';
|
|
||||||
|
|
||||||
if (!Filesystem::binaryExists('mxmlc')) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
"The `mxmlc` binary was not found in PATH. This compiler binary ".
|
|
||||||
"is required to rebuild the Aphlict client.\n\n".
|
|
||||||
"Adjust your PATH, or install the Flex SDK from:\n\n".
|
|
||||||
" http://flex.apache.org\n\n".
|
|
||||||
"You may also be able to install it with `npm`:\n\n".
|
|
||||||
" $ npm install flex-sdk\n\n".
|
|
||||||
"(Note: you should only need to rebuild Aphlict if you are ".
|
|
||||||
"developing Phabricator.)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
$argv = array(
|
|
||||||
"-source-path=$root/externals/vegas/src",
|
|
||||||
'-static-link-runtime-shared-libraries=true',
|
|
||||||
'-warnings=true',
|
|
||||||
'-strict=true',
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($args->getArg('debug')) {
|
|
||||||
$argv[] = '-debug=true';
|
|
||||||
}
|
|
||||||
|
|
||||||
list ($err, $stdout, $stderr) = exec_manual('mxmlc %Ls -output=%s %s',
|
|
||||||
$argv,
|
|
||||||
$root.'/webroot/rsrc/swf/aphlict.swf',
|
|
||||||
$root.'/support/aphlict/client/src/AphlictClient.as');
|
|
||||||
|
|
||||||
if ($err) {
|
|
||||||
$console->writeErr($stderr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$console->writeOut("Done.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ final class PhabricatorAphlictManagementDebugWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$this->willLaunch();
|
$this->willLaunch(true);
|
||||||
return $this->launch(true);
|
return $this->launch(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ abstract class PhabricatorAphlictManagementWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function willLaunch() {
|
final protected function willLaunch($debug = false) {
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
$pid = $this->getPID();
|
$pid = $this->getPID();
|
||||||
|
@ -61,15 +61,66 @@ abstract class PhabricatorAphlictManagementWorkflow
|
||||||
'running. Use `aphlict restart` to restart it.'));
|
'running. Use `aphlict restart` to restart it.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (posix_getuid() != 0) {
|
if (posix_getuid() == 0) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'You must run this script as root; the Aphlict server needs to bind '.
|
// TODO: Update this message after a while.
|
||||||
'to privileged ports.'));
|
'The notification server should not be run as root. It no '.
|
||||||
|
'longer requires access to privileged ports.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will throw if we can't find an appropriate `node`.
|
// Make sure we can write to the PID file.
|
||||||
$this->getNodeBinary();
|
if (!$debug) {
|
||||||
|
Filesystem::writeFile($this->getPIDPath(), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, start the server in configuration test mode with --test. This
|
||||||
|
// will let us error explicitly if there are missing modules, before we
|
||||||
|
// fork and lose access to the console.
|
||||||
|
$test_argv = $this->getServerArgv($debug);
|
||||||
|
$test_argv[] = '--test=true';
|
||||||
|
|
||||||
|
execx(
|
||||||
|
'%s %s %Ls',
|
||||||
|
$this->getNodeBinary(),
|
||||||
|
$this->getAphlictScriptPath(),
|
||||||
|
$test_argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getServerArgv($debug) {
|
||||||
|
$ssl_key = PhabricatorEnv::getEnvConfig('notification.ssl-key');
|
||||||
|
$ssl_cert = PhabricatorEnv::getEnvConfig('notification.ssl-cert');
|
||||||
|
|
||||||
|
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
||||||
|
$server_uri = new PhutilURI($server_uri);
|
||||||
|
|
||||||
|
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
||||||
|
$client_uri = new PhutilURI($client_uri);
|
||||||
|
|
||||||
|
$log = PhabricatorEnv::getEnvConfig('notification.log');
|
||||||
|
|
||||||
|
$server_argv = array();
|
||||||
|
$server_argv[] = '--port='.$client_uri->getPort();
|
||||||
|
$server_argv[] = '--admin='.$server_uri->getPort();
|
||||||
|
|
||||||
|
if ($ssl_key) {
|
||||||
|
$server_argv[] = '--ssl-key='.$ssl_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ssl_cert) {
|
||||||
|
$server_argv[] = '--ssl-cert='.$ssl_cert;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$debug) {
|
||||||
|
$server_argv[] = '--log='.$log;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $server_argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAphlictScriptPath() {
|
||||||
|
$root = dirname(phutil_get_library_root('phabricator'));
|
||||||
|
return $root.'/support/aphlict/server/aphlict_server.js';
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function launch($debug = false) {
|
final protected function launch($debug = false) {
|
||||||
|
@ -81,33 +132,11 @@ abstract class PhabricatorAphlictManagementWorkflow
|
||||||
Filesystem::writeFile($this->getPIDPath(), getmypid());
|
Filesystem::writeFile($this->getPIDPath(), getmypid());
|
||||||
}
|
}
|
||||||
|
|
||||||
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
|
||||||
$server_uri = new PhutilURI($server_uri);
|
|
||||||
|
|
||||||
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
|
||||||
$client_uri = new PhutilURI($client_uri);
|
|
||||||
|
|
||||||
$user = PhabricatorEnv::getEnvConfig('notification.user');
|
|
||||||
$log = PhabricatorEnv::getEnvConfig('notification.log');
|
|
||||||
|
|
||||||
$server_argv = array();
|
|
||||||
$server_argv[] = csprintf('--port=%s', $client_uri->getPort());
|
|
||||||
$server_argv[] = csprintf('--admin=%s', $server_uri->getPort());
|
|
||||||
$server_argv[] = csprintf('--host=%s', $server_uri->getDomain());
|
|
||||||
|
|
||||||
if ($user) {
|
|
||||||
$server_argv[] = csprintf('--user=%s', $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$debug) {
|
|
||||||
$server_argv[] = csprintf('--log=%s', $log);
|
|
||||||
}
|
|
||||||
|
|
||||||
$command = csprintf(
|
$command = csprintf(
|
||||||
'%s %s %C',
|
'%s %s %Ls',
|
||||||
$this->getNodeBinary(),
|
$this->getNodeBinary(),
|
||||||
dirname(__FILE__).'/../../../../support/aphlict/server/aphlict_server.js',
|
$this->getAphlictScriptPath(),
|
||||||
implode(' ', $server_argv));
|
$this->getServerArgv($debug));
|
||||||
|
|
||||||
if (!$debug) {
|
if (!$debug) {
|
||||||
declare(ticks = 1);
|
declare(ticks = 1);
|
||||||
|
@ -159,6 +188,7 @@ abstract class PhabricatorAphlictManagementWorkflow
|
||||||
fclose(STDOUT);
|
fclose(STDOUT);
|
||||||
fclose(STDERR);
|
fclose(STDERR);
|
||||||
|
|
||||||
|
|
||||||
$this->launch();
|
$this->launch();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,11 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||||
'This option has been renamed to `phabricator.show-prototypes` '.
|
'This option has been renamed to `phabricator.show-prototypes` '.
|
||||||
'to emphasize the unfinished nature of many prototype applications. '.
|
'to emphasize the unfinished nature of many prototype applications. '.
|
||||||
'Your existing setting has been migrated.'),
|
'Your existing setting has been migrated.'),
|
||||||
|
'notification.user' => pht(
|
||||||
|
'The notification server no longer requires root permissions. Start '.
|
||||||
|
'the server as the user you want it to run under.'),
|
||||||
|
'notification.debug' => pht(
|
||||||
|
'Notifications no longer have a dedicated debugging mode.'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $ancient_config;
|
return $ancient_config;
|
||||||
|
|
|
@ -36,23 +36,21 @@ final class PhabricatorNotificationConfigOptions
|
||||||
'string',
|
'string',
|
||||||
'http://localhost:22281/')
|
'http://localhost:22281/')
|
||||||
->setDescription(pht('Location of the notification receiver server.')),
|
->setDescription(pht('Location of the notification receiver server.')),
|
||||||
$this->newOption('notification.user', 'string', null)
|
|
||||||
->setSummary(pht('Drop permissions to a less-privileged user.'))
|
|
||||||
->setDescription(
|
|
||||||
pht(
|
|
||||||
'The notifcation server must be started as root so it can bind '.
|
|
||||||
'to privileged ports, but if you specify a system user here it '.
|
|
||||||
'will drop permissions to that user after binding to the ports '.
|
|
||||||
'it needs.')),
|
|
||||||
$this->newOption('notification.log', 'string', '/var/log/aphlict.log')
|
$this->newOption('notification.log', 'string', '/var/log/aphlict.log')
|
||||||
->setDescription(pht('Location of the server log file.')),
|
->setDescription(pht('Location of the server log file.')),
|
||||||
|
$this->newOption('notification.ssl-key', 'string', null)
|
||||||
|
->setLocked(true)
|
||||||
|
->setDescription(
|
||||||
|
pht('Path to SSL key to use for secure WebSockets.')),
|
||||||
|
$this->newOption('notification.ssl-cert', 'string', null)
|
||||||
|
->setLocked(true)
|
||||||
|
->setDescription(
|
||||||
|
pht('Path to SSL certificate to use for secure WebSockets.')),
|
||||||
$this->newOption(
|
$this->newOption(
|
||||||
'notification.pidfile',
|
'notification.pidfile',
|
||||||
'string',
|
'string',
|
||||||
'/var/run/aphlict.pid')
|
'/var/tmp/aphlict/pid/aphlict.pid')
|
||||||
->setDescription(pht('Location of the server PID file.')),
|
->setDescription(pht('Location of the server PID file.')),
|
||||||
$this->newOption('notification.debug', 'bool', false)
|
|
||||||
->setDescription(pht('Enable debug output in the browser.')),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,11 @@ final class PhabricatorNotificationTestController
|
||||||
|
|
||||||
$viewer_phid = $viewer->getPHID();
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
|
||||||
|
// NOTE: Because we don't currently show you your own notifications, make
|
||||||
|
// sure this comes from a different PHID.
|
||||||
|
$application_phid = id(new PhabricatorNotificationsApplication())
|
||||||
|
->getPHID();
|
||||||
|
|
||||||
// TODO: When it's easier to get these buttons to render as forms, this
|
// TODO: When it's easier to get these buttons to render as forms, this
|
||||||
// would be slightly nicer as a more standard isFormPost() check.
|
// would be slightly nicer as a more standard isFormPost() check.
|
||||||
|
|
||||||
|
@ -24,7 +29,7 @@ final class PhabricatorNotificationTestController
|
||||||
->setStoryType($story_type)
|
->setStoryType($story_type)
|
||||||
->setStoryData($story_data)
|
->setStoryData($story_data)
|
||||||
->setStoryTime(time())
|
->setStoryTime(time())
|
||||||
->setStoryAuthorPHID($viewer_phid)
|
->setStoryAuthorPHID($application_phid)
|
||||||
->setRelatedPHIDs(array($viewer_phid))
|
->setRelatedPHIDs(array($viewer_phid))
|
||||||
->setPrimaryObjectPHID($viewer_phid)
|
->setPrimaryObjectPHID($viewer_phid)
|
||||||
->setSubscribedPHIDs(array($viewer_phid))
|
->setSubscribedPHIDs(array($viewer_phid))
|
||||||
|
|
|
@ -38,7 +38,7 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck {
|
||||||
->addCommand(
|
->addCommand(
|
||||||
pht(
|
pht(
|
||||||
"(To start the server, run this command.)\n".
|
"(To start the server, run this command.)\n".
|
||||||
"phabricator/ $ sudo ./bin/aphlict start"));
|
"phabricator/ $ ./bin/aphlict start"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck {
|
||||||
->setShortName(pht('Notification Server Version'))
|
->setShortName(pht('Notification Server Version'))
|
||||||
->setName(pht('Notification Server Out of Date'))
|
->setName(pht('Notification Server Out of Date'))
|
||||||
->setMessage($message)
|
->setMessage($message)
|
||||||
->addCommand('phabricator/ $ sudo ./bin/aphlict restart');
|
->addCommand('phabricator/ $ ./bin/aphlict restart');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,8 @@ final class PhabricatorNotificationStatusView extends AphrontTagView {
|
||||||
'nodeID' => $this->getID(),
|
'nodeID' => $this->getID(),
|
||||||
'pht' => array(
|
'pht' => array(
|
||||||
'setup' => pht('Setting Up Client'),
|
'setup' => pht('Setting Up Client'),
|
||||||
'start' => pht('Starting Client'),
|
'open' => pht('Connected'),
|
||||||
'ready' => pht('Ready to Connect'),
|
'closed' => pht('Disconnected'),
|
||||||
'connecting' => pht('Connecting...'),
|
|
||||||
'connected' => pht('Connected'),
|
|
||||||
'error' => pht('Connection Error'),
|
|
||||||
'client' => pht('Connected Locally'),
|
|
||||||
|
|
||||||
'error.flash.xdomain' => pht(
|
|
||||||
'Unable to connect to Flash Policy Server. Check that the '.
|
|
||||||
'notification server is running and port 843 is not firewalled.'),
|
|
||||||
'error.flash.disconnected' => pht(
|
|
||||||
'Disconnected from notification server.'),
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
Guide to setting up notifications.
|
Guide to setting up notifications.
|
||||||
|
|
||||||
= Overview =
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
By default, Phabricator delivers information about events (like users creating
|
By default, Phabricator delivers information about events (like users creating
|
||||||
tasks or commenting on code reviews) through email and in-application
|
tasks or commenting on code reviews) through email and in-application
|
||||||
|
@ -20,61 +21,95 @@ To enable real-time notifications:
|
||||||
|
|
||||||
This document describes the process in detail.
|
This document describes the process in detail.
|
||||||
|
|
||||||
= Running the Aphlict Server =
|
|
||||||
|
|
||||||
Phabricator implements realtime notifications using a Node.js server called
|
Supported Browsers
|
||||||
"Aphlict". To run it:
|
==================
|
||||||
|
|
||||||
- Install node.js.
|
Notifications are supported for browsers which support WebSockets. This covers
|
||||||
- Run `bin/aphlict start` (this script must be run as root).
|
most modern browsers (like Chrome, Firefox, Safari, and recent versions of
|
||||||
|
Internet Explorer) and many mobile browsers.
|
||||||
|
|
||||||
The server must be able to listen on port **843** and port **22280** for Aphlict
|
IE8 and IE9 do not support WebSockets, so real-time notifications won't work in
|
||||||
to work. You can change the latter port in the `notification.client-uri` config,
|
those browsers.
|
||||||
but port 843 is used by Flash and can not be changed. In particular, if you're
|
|
||||||
running in EC2, you need to unblock both of these ports in the server's security
|
|
||||||
group configuration.
|
|
||||||
|
|
||||||
You may want to adjust these settings:
|
|
||||||
|
Installing Node and Modules
|
||||||
|
===========================
|
||||||
|
|
||||||
|
The notification server uses Node.js, so you'll need to install it first.
|
||||||
|
|
||||||
|
To install Node.js, follow the instructions on
|
||||||
|
[[ http://nodejs.org | nodejs.org ]].
|
||||||
|
|
||||||
|
You will also need to install the `ws` module for Node. After installing
|
||||||
|
Node, run `npm install -g ws` to install it.
|
||||||
|
|
||||||
|
name="(Option 1, Recommended) Install 'ws' Module Globally"
|
||||||
|
$ npm install -g ws # Global Install
|
||||||
|
|
||||||
|
If you prefer, you can also install it locally in the `support/aphlict/server/`
|
||||||
|
directory:
|
||||||
|
|
||||||
|
name="(Option 2) Install 'ws' Module Locally"
|
||||||
|
phabricator/support/aphlict/server/ $ npm install ws
|
||||||
|
|
||||||
|
Once Node.js and the `ws` module are installed, you're ready to start the
|
||||||
|
server.
|
||||||
|
|
||||||
|
|
||||||
|
Running the Aphlict Server
|
||||||
|
==========================
|
||||||
|
|
||||||
|
After installing Node.js, you can control the notification server with the
|
||||||
|
`bin/aphlict` command. To start the server:
|
||||||
|
|
||||||
|
phabricator/ $ bin/aphlict start
|
||||||
|
|
||||||
|
The server must be able to listen on port **22280** for Aphlict to work. In
|
||||||
|
particular, if you're running in EC2, you need to unblock this port in the
|
||||||
|
server's security group configuration. You can change this port in the
|
||||||
|
`notification.client-uri` config.
|
||||||
|
|
||||||
|
You may need to adjust these settings:
|
||||||
|
|
||||||
|
- `notification.ssl-cert` Point this at an SSL certificate for secure
|
||||||
|
WebSockets.
|
||||||
|
- `notification.ssl-key` Point this at an SSL keyfile for secure WebSockets.
|
||||||
|
|
||||||
|
In particular, if your server uses HTTPS, you **must** configure these options.
|
||||||
|
Browsers will not allow you to use non-SSL websockets from an SSL web page.
|
||||||
|
|
||||||
|
You may also want to adjust these settings:
|
||||||
|
|
||||||
- `notification.client-uri` Externally-facing host and port that browsers will
|
- `notification.client-uri` Externally-facing host and port that browsers will
|
||||||
connect to in order to listen for notifications.
|
connect to in order to listen for notifications.
|
||||||
- `notification.server-uri` Internally-facing host and port that Phabricator
|
- `notification.server-uri` Internally-facing host and port that Phabricator
|
||||||
will connect to in order to publish notifications.
|
will connect to in order to publish notifications.
|
||||||
- `notification.log` Log file location for the server.
|
- `notification.log` Log file location for the server.
|
||||||
- `notification.user` Non-root user to drop permissions to after binding to
|
|
||||||
privileged ports.
|
|
||||||
- `notification.pid` Pidfile location used to stop any running server when
|
- `notification.pid` Pidfile location used to stop any running server when
|
||||||
aphlict is restarted.
|
aphlict is restarted.
|
||||||
|
|
||||||
In most cases, the defaults are appropriate, except that you should set
|
|
||||||
`notification.user` to some valid system user so Aphlict isn't running as root.
|
|
||||||
|
|
||||||
== Verifying Server Status ==
|
Verifying Server Status
|
||||||
|
=======================
|
||||||
|
|
||||||
Access `/notification/status/` to verify the server is operational. You should
|
Access `/notification/status/` to verify the server is operational. You should
|
||||||
see a table showing stats like "uptime" and connection/message counts if the
|
see a table showing stats like "uptime" and connection/message counts if the
|
||||||
server is working. If it isn't working, you should see an error.
|
server is working. If it isn't working, you should see an error.
|
||||||
|
|
||||||
== Testing the Server ==
|
You can also send a test notification by clicking the button in the upper right
|
||||||
|
corner of this screen.
|
||||||
|
|
||||||
The easiest way to test the server is to have two users login and comment on
|
|
||||||
the same Maniphest Task or Differential Revision. They should receive in-browser
|
|
||||||
notifications about the other user's activity.
|
|
||||||
|
|
||||||
NOTE: This is cumbersome. There will be better testing tools at some point.
|
Troubleshooting
|
||||||
|
===============
|
||||||
== Debugging Server Problems ==
|
|
||||||
|
|
||||||
You can run `aphlict` in the foreground to get output to your console:
|
You can run `aphlict` in the foreground to get output to your console:
|
||||||
|
|
||||||
phabricator/ $ sudo ./bin/aphlict debug
|
phabricator/ $ ./bin/aphlict debug
|
||||||
|
|
||||||
You can run `support/aphlict/client/aphlict_test_client.php` to connect to the
|
Because the notification server uses WebSockets, your browser error console
|
||||||
Aphlict server from the command line. Messages the client receives will be
|
may also have information that is useful in figuring out what's wrong.
|
||||||
printed to stdout.
|
|
||||||
|
|
||||||
You can set `notification.debug` in your configuration to get additional
|
|
||||||
output in your browser.
|
|
||||||
|
|
||||||
The server also generates a log, by default in `/var/log/aphlict.log`. You can
|
The server also generates a log, by default in `/var/log/aphlict.log`. You can
|
||||||
change this location by changing `notification.log` in your configuration. The
|
change this location by changing `notification.log` in your configuration. The
|
||||||
|
|
|
@ -371,9 +371,6 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
||||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||||
if ($user && $user->isLoggedIn()) {
|
if ($user && $user->isLoggedIn()) {
|
||||||
|
|
||||||
$aphlict_object_id = celerity_generate_unique_node_id();
|
|
||||||
$aphlict_container_id = celerity_generate_unique_node_id();
|
|
||||||
|
|
||||||
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
||||||
$client_uri = new PhutilURI($client_uri);
|
$client_uri = new PhutilURI($client_uri);
|
||||||
if ($client_uri->getDomain() == 'localhost') {
|
if ($client_uri->getDomain() == 'localhost') {
|
||||||
|
@ -382,37 +379,24 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
||||||
$client_uri->setDomain($this_host->getDomain());
|
$client_uri->setDomain($this_host->getDomain());
|
||||||
}
|
}
|
||||||
|
|
||||||
$map = CelerityResourceMap::getNamedInstance('phabricator');
|
|
||||||
$swf_uri = $response->getURI($map, 'rsrc/swf/aphlict.swf', true);
|
|
||||||
|
|
||||||
$enable_debug = PhabricatorEnv::getEnvConfig('notification.debug');
|
|
||||||
|
|
||||||
$subscriptions = $this->pageObjects;
|
$subscriptions = $this->pageObjects;
|
||||||
if ($user) {
|
if ($user) {
|
||||||
$subscriptions[] = $user->getPHID();
|
$subscriptions[] = $user->getPHID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->isHTTPS()) {
|
||||||
|
$client_uri->setProtocol('wss');
|
||||||
|
} else {
|
||||||
|
$client_uri->setProtocol('ws');
|
||||||
|
}
|
||||||
|
|
||||||
Javelin::initBehavior(
|
Javelin::initBehavior(
|
||||||
'aphlict-listen',
|
'aphlict-listen',
|
||||||
array(
|
array(
|
||||||
'id' => $aphlict_object_id,
|
'websocketURI' => (string)$client_uri,
|
||||||
'containerID' => $aphlict_container_id,
|
|
||||||
'server' => $client_uri->getDomain(),
|
|
||||||
'port' => $client_uri->getPort(),
|
|
||||||
'debug' => $enable_debug,
|
|
||||||
'swfURI' => $swf_uri,
|
|
||||||
'pageObjects' => array_fill_keys($this->pageObjects, true),
|
'pageObjects' => array_fill_keys($this->pageObjects, true),
|
||||||
'subscriptions' => $subscriptions,
|
'subscriptions' => $subscriptions,
|
||||||
));
|
));
|
||||||
|
|
||||||
$tail[] = phutil_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'id' => $aphlict_container_id,
|
|
||||||
'style' =>
|
|
||||||
'position: absolute; width: 0; height: 0; overflow: hidden;',
|
|
||||||
),
|
|
||||||
'');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
|
||||||
require_once $root.'/scripts/__init_script__.php';
|
|
||||||
|
|
||||||
$args = new PhutilArgumentParser($argv);
|
|
||||||
$args->setTagline('test client for Aphlict server');
|
|
||||||
$args->setSynopsis(<<<EOHELP
|
|
||||||
**aphlict_test_client.php** [__options__]
|
|
||||||
Connect to the Aphlict server configured in the Phabricator config.
|
|
||||||
|
|
||||||
EOHELP
|
|
||||||
);
|
|
||||||
$args->parseStandardArguments();
|
|
||||||
$args->parse(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'server',
|
|
||||||
'param' => 'uri',
|
|
||||||
'default' => PhabricatorEnv::getEnvConfig('notification.client-uri'),
|
|
||||||
'help' => 'Connect to __uri__ instead of the default server.',
|
|
||||||
),
|
|
||||||
));
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
|
|
||||||
$errno = null;
|
|
||||||
$errstr = null;
|
|
||||||
|
|
||||||
$uri = $args->getArg('server');
|
|
||||||
$uri = new PhutilURI($uri);
|
|
||||||
$uri->setProtocol('tcp');
|
|
||||||
|
|
||||||
$console->writeErr("Connecting...\n");
|
|
||||||
$socket = stream_socket_client(
|
|
||||||
$uri,
|
|
||||||
$errno,
|
|
||||||
$errstr);
|
|
||||||
|
|
||||||
if (!$socket) {
|
|
||||||
$console->writeErr(
|
|
||||||
"Unable to connect to Aphlict (at '$uri'). Error #{$errno}: {$errstr}");
|
|
||||||
exit(1);
|
|
||||||
} else {
|
|
||||||
$console->writeErr("Connected.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$io_channel = new PhutilSocketChannel($socket);
|
|
||||||
$proto_channel = new PhutilJSONProtocolChannel($io_channel);
|
|
||||||
|
|
||||||
$json = new PhutilJSON();
|
|
||||||
while (true) {
|
|
||||||
$message = $proto_channel->waitForMessage();
|
|
||||||
$console->writeOut($json->encodeFormatted($message));
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package {
|
|
||||||
|
|
||||||
import flash.display.Sprite;
|
|
||||||
import flash.external.ExternalInterface;
|
|
||||||
import flash.net.LocalConnection;
|
|
||||||
|
|
||||||
|
|
||||||
public class Aphlict extends Sprite {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A transport channel used to receive data.
|
|
||||||
*/
|
|
||||||
protected var recv:LocalConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A transport channel used to send data.
|
|
||||||
*/
|
|
||||||
protected var send:LocalConnection;
|
|
||||||
|
|
||||||
|
|
||||||
public function Aphlict() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.recv = new LocalConnection();
|
|
||||||
this.recv.client = this;
|
|
||||||
|
|
||||||
this.send = new LocalConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
final protected function externalInvoke(
|
|
||||||
type:String,
|
|
||||||
object:Object = null):void {
|
|
||||||
|
|
||||||
ExternalInterface.call('JX.Aphlict.didReceiveEvent', type, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
final protected function error(error:Object):void {
|
|
||||||
this.externalInvoke('error', error.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
final protected function log(message:String):void {
|
|
||||||
this.externalInvoke('log', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,160 +0,0 @@
|
||||||
package {
|
|
||||||
|
|
||||||
import flash.events.TimerEvent;
|
|
||||||
import flash.external.ExternalInterface;
|
|
||||||
import flash.utils.Dictionary;
|
|
||||||
import flash.utils.Timer;
|
|
||||||
import flash.events.UncaughtErrorEvent;
|
|
||||||
|
|
||||||
final public class AphlictClient extends Aphlict {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection name for this client. This will be used for the
|
|
||||||
* @{class:LocalConnection} object.
|
|
||||||
*/
|
|
||||||
private var client:String;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The expiry timestamp for the @{class:AphlictMaster}. If this time is
|
|
||||||
* elapsed then the master will be assumed to be dead and another
|
|
||||||
* @{class:AphlictClient} will create a master.
|
|
||||||
*/
|
|
||||||
private var expiry:Number = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The interval at which to ping the @{class:AphlictMaster}.
|
|
||||||
*/
|
|
||||||
public static const INTERVAL:Number = 3000;
|
|
||||||
|
|
||||||
private var master:AphlictMaster;
|
|
||||||
private var timer:Timer;
|
|
||||||
|
|
||||||
private var remoteServer:String;
|
|
||||||
private var remotePort:Number;
|
|
||||||
private var subscriptions:Array;
|
|
||||||
|
|
||||||
|
|
||||||
public function AphlictClient() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
loaderInfo.uncaughtErrorEvents.addEventListener(
|
|
||||||
UncaughtErrorEvent.UNCAUGHT_ERROR,
|
|
||||||
this.uncaughtErrorHandler);
|
|
||||||
|
|
||||||
ExternalInterface.marshallExceptions = true;
|
|
||||||
ExternalInterface.addCallback('connect', this.externalConnect);
|
|
||||||
|
|
||||||
this.setStatus('ready');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function uncaughtErrorHandler(event:UncaughtErrorEvent):void {
|
|
||||||
this.error(event.error.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function externalConnect(
|
|
||||||
server:String,
|
|
||||||
port:Number,
|
|
||||||
subscriptions:Array):void {
|
|
||||||
|
|
||||||
this.remoteServer = server;
|
|
||||||
this.remotePort = port;
|
|
||||||
this.subscriptions = subscriptions;
|
|
||||||
|
|
||||||
this.client = AphlictClient.generateClientId();
|
|
||||||
this.recv.connect(this.client);
|
|
||||||
|
|
||||||
this.timer = new Timer(AphlictClient.INTERVAL);
|
|
||||||
this.timer.addEventListener(TimerEvent.TIMER, this.keepalive);
|
|
||||||
|
|
||||||
this.connectToMaster();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a unique identifier that will be used to communicate with the
|
|
||||||
* @{class:AphlictMaster}.
|
|
||||||
*/
|
|
||||||
private static function generateClientId():String {
|
|
||||||
return 'aphlict_client_' + Math.round(Math.random() * 100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new connection to the @{class:AphlictMaster}.
|
|
||||||
*
|
|
||||||
* If there is no current @{class:AphlictMaster} instance, then a new master
|
|
||||||
* will be created.
|
|
||||||
*/
|
|
||||||
private function connectToMaster():void {
|
|
||||||
this.timer.stop();
|
|
||||||
|
|
||||||
// Try to become the master.
|
|
||||||
try {
|
|
||||||
this.log('Attempting to become the master...');
|
|
||||||
this.master = new AphlictMaster(this.remoteServer, this.remotePort);
|
|
||||||
this.log('I am the master.');
|
|
||||||
} catch (err:ArgumentError) {
|
|
||||||
this.log('Cannot become the master... probably one already exists');
|
|
||||||
} catch (err:Error) {
|
|
||||||
this.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.registerWithMaster();
|
|
||||||
this.timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register our client ID with the @{class:AphlictMaster} and send our
|
|
||||||
* subscriptions.
|
|
||||||
*/
|
|
||||||
private function registerWithMaster():void {
|
|
||||||
this.send.send('aphlict_master', 'register', this.client);
|
|
||||||
this.expiry = new Date().getTime() + (5 * AphlictClient.INTERVAL);
|
|
||||||
this.log('Registered client ' + this.client);
|
|
||||||
|
|
||||||
// Send subscriptions to master.
|
|
||||||
this.log('Sending subscriptions to master.');
|
|
||||||
this.send.send(
|
|
||||||
'aphlict_master',
|
|
||||||
'subscribe',
|
|
||||||
this.client,
|
|
||||||
this.subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a keepalive signal to the @{class:AphlictMaster}.
|
|
||||||
*
|
|
||||||
* If the connection to the master has expired (because the master has not
|
|
||||||
* sent a heartbeat signal), then a new connection to master will be
|
|
||||||
* created.
|
|
||||||
*/
|
|
||||||
private function keepalive(event:TimerEvent):void {
|
|
||||||
if (new Date().getTime() > this.expiry) {
|
|
||||||
this.connectToMaster();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send.send('aphlict_master', 'ping', this.client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is used to receive the heartbeat signal from the
|
|
||||||
* @{class:AphlictMaster}.
|
|
||||||
*/
|
|
||||||
public function pong():void {
|
|
||||||
this.expiry = new Date().getTime() + (2 * AphlictClient.INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive a message from the Aphlict Server, via the
|
|
||||||
* @{class:AphlictMaster}.
|
|
||||||
*/
|
|
||||||
public function receiveMessage(msg:Object):void {
|
|
||||||
this.log('Received message.');
|
|
||||||
this.externalInvoke('receive', msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setStatus(status:String, code:String = null):void {
|
|
||||||
this.externalInvoke('status', {type: status, code: code});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
package {
|
|
||||||
|
|
||||||
import flash.events.Event;
|
|
||||||
import flash.events.IOErrorEvent;
|
|
||||||
import flash.events.ProgressEvent;
|
|
||||||
import flash.events.SecurityErrorEvent;
|
|
||||||
import flash.events.TimerEvent;
|
|
||||||
import flash.net.Socket;
|
|
||||||
import flash.utils.ByteArray;
|
|
||||||
import flash.utils.Dictionary;
|
|
||||||
import flash.utils.Timer;
|
|
||||||
import vegas.strings.JSON;
|
|
||||||
|
|
||||||
|
|
||||||
final public class AphlictMaster extends Aphlict {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The pool of connected clients.
|
|
||||||
*/
|
|
||||||
private var clients:Dictionary;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A timer used to trigger periodic events.
|
|
||||||
*/
|
|
||||||
private var timer:Timer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The interval after which clients will be considered dead and removed
|
|
||||||
* from the pool.
|
|
||||||
*/
|
|
||||||
public static const PURGE_INTERVAL:Number = 3 * AphlictClient.INTERVAL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The hostname for the Aphlict Server.
|
|
||||||
*/
|
|
||||||
private var remoteServer:String;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port number for the Aphlict Server.
|
|
||||||
*/
|
|
||||||
private var remotePort:Number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A dictionary mapping PHID to subscribed clients.
|
|
||||||
*/
|
|
||||||
private var subscriptions:Dictionary;
|
|
||||||
|
|
||||||
private var socket:Socket;
|
|
||||||
private var readBuffer:ByteArray;
|
|
||||||
|
|
||||||
private var status:String;
|
|
||||||
private var statusCode:String;
|
|
||||||
|
|
||||||
|
|
||||||
public function AphlictMaster(server:String, port:Number) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.remoteServer = server;
|
|
||||||
this.remotePort = port;
|
|
||||||
|
|
||||||
this.clients = new Dictionary();
|
|
||||||
this.subscriptions = new Dictionary();
|
|
||||||
|
|
||||||
// Connect to the Aphlict Server.
|
|
||||||
this.recv.connect('aphlict_master');
|
|
||||||
this.connectToServer();
|
|
||||||
|
|
||||||
// Start a timer and regularly purge dead clients.
|
|
||||||
this.timer = new Timer(AphlictMaster.PURGE_INTERVAL);
|
|
||||||
this.timer.addEventListener(TimerEvent.TIMER, this.purgeClients);
|
|
||||||
this.timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a @{class:AphlictClient}.
|
|
||||||
*/
|
|
||||||
public function register(client:String):void {
|
|
||||||
if (!this.clients[client]) {
|
|
||||||
this.log('Registering client: ' + client);
|
|
||||||
this.clients[client] = new Date().getTime();
|
|
||||||
|
|
||||||
this.send.send(client, 'setStatus', this.status, this.statusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Purge stale client connections from the client pool.
|
|
||||||
*/
|
|
||||||
private function purgeClients(event:TimerEvent):void {
|
|
||||||
for (var client:String in this.clients) {
|
|
||||||
var checkin:Number = this.clients[client];
|
|
||||||
|
|
||||||
if (new Date().getTime() - checkin > AphlictMaster.PURGE_INTERVAL) {
|
|
||||||
this.log('Purging client: ' + client);
|
|
||||||
delete this.clients[client];
|
|
||||||
|
|
||||||
this.log('Removing client subscriptions: ' + client);
|
|
||||||
this.unsubscribeAll(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clients will regularly "ping" the master to let us know that they are
|
|
||||||
* still alive. We will "pong" them back to let the client know that the
|
|
||||||
* master is still alive.
|
|
||||||
*/
|
|
||||||
public function ping(client:String):void {
|
|
||||||
this.clients[client] = new Date().getTime();
|
|
||||||
this.send.send(client, 'pong');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function connectToServer():void {
|
|
||||||
this.setStatusOnClients('connecting');
|
|
||||||
|
|
||||||
var socket:Socket = new Socket();
|
|
||||||
|
|
||||||
socket.addEventListener(Event.CONNECT, didConnectSocket);
|
|
||||||
socket.addEventListener(Event.CLOSE, didCloseSocket);
|
|
||||||
socket.addEventListener(ProgressEvent.SOCKET_DATA, didReceiveSocket);
|
|
||||||
|
|
||||||
socket.addEventListener(IOErrorEvent.IO_ERROR, didIOErrorSocket);
|
|
||||||
socket.addEventListener(
|
|
||||||
SecurityErrorEvent.SECURITY_ERROR,
|
|
||||||
didSecurityErrorSocket);
|
|
||||||
|
|
||||||
socket.connect(this.remoteServer, this.remotePort);
|
|
||||||
|
|
||||||
this.readBuffer = new ByteArray();
|
|
||||||
this.socket = socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function didConnectSocket(event:Event):void {
|
|
||||||
this.setStatusOnClients('connected');
|
|
||||||
|
|
||||||
// Send subscriptions
|
|
||||||
var phids = new Array();
|
|
||||||
for (var phid:String in this.subscriptions) {
|
|
||||||
phids.push(phid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phids.length) {
|
|
||||||
this.sendSubscribeCommand(phids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function didCloseSocket(event:Event):void {
|
|
||||||
this.setStatusOnClients('error', 'error.flash.disconnected');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function didIOErrorSocket(event:IOErrorEvent):void {
|
|
||||||
this.externalInvoke('error', event.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function didSecurityErrorSocket(event:SecurityErrorEvent):void {
|
|
||||||
var text = event.text;
|
|
||||||
|
|
||||||
// This is really gross but there doesn't seem to be anything else
|
|
||||||
// on the object which gives us an error code.
|
|
||||||
if (text.match(/^Error #2048/)) {
|
|
||||||
this.setStatusOnClients('error', 'error.flash.xdomain');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function subscribe(client:String, phids:Array):void {
|
|
||||||
var newPHIDs = new Array();
|
|
||||||
|
|
||||||
for (var i:String in phids) {
|
|
||||||
var phid = phids[i];
|
|
||||||
if (!this.subscriptions[phid]) {
|
|
||||||
this.subscriptions[phid] = new Dictionary();
|
|
||||||
newPHIDs.push(phid);
|
|
||||||
}
|
|
||||||
this.subscriptions[phid][client] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newPHIDs.length) {
|
|
||||||
this.sendSubscribeCommand(newPHIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getSubscriptions(client:String):Array {
|
|
||||||
var subscriptions = new Array();
|
|
||||||
|
|
||||||
for (var phid:String in this.subscriptions) {
|
|
||||||
var clients = this.subscriptions[phid];
|
|
||||||
if (clients[client]) {
|
|
||||||
subscriptions.push(phid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unsubscribeAll(client:String):void {
|
|
||||||
this.unsubscribe(client, this.getSubscriptions(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unsubscribe(client:String, phids:Array):void {
|
|
||||||
var oldPHIDs = new Array();
|
|
||||||
|
|
||||||
for (var i:String in phids) {
|
|
||||||
var phid = phids[i];
|
|
||||||
|
|
||||||
if (!this.subscriptions[phid]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this.subscriptions[phid][client];
|
|
||||||
|
|
||||||
var empty = true;
|
|
||||||
for (var key:String in this.subscriptions[phid]) {
|
|
||||||
empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
delete this.subscriptions[phid];
|
|
||||||
oldPHIDs.push(phid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldPHIDs.length) {
|
|
||||||
this.sendUnsubscribeCommand(oldPHIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendSubscribeCommand(phids:Array):void {
|
|
||||||
var msg:Dictionary = new Dictionary();
|
|
||||||
msg['command'] = 'subscribe';
|
|
||||||
msg['data'] = phids;
|
|
||||||
|
|
||||||
this.log('Sending subscribe command to server.');
|
|
||||||
this.socket.writeUTF(vegas.strings.JSON.serialize(msg));
|
|
||||||
this.socket.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendUnsubscribeCommand(phids:Array):void {
|
|
||||||
var msg:Dictionary = new Dictionary();
|
|
||||||
msg['command'] = 'unsubscribe';
|
|
||||||
msg['data'] = phids;
|
|
||||||
|
|
||||||
this.log('Sending subscribe command to server.');
|
|
||||||
this.socket.writeUTF(vegas.strings.JSON.serialize(msg));
|
|
||||||
this.socket.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function didReceiveSocket(event:Event):void {
|
|
||||||
try {
|
|
||||||
var b:ByteArray = this.readBuffer;
|
|
||||||
this.socket.readBytes(b, b.length);
|
|
||||||
|
|
||||||
do {
|
|
||||||
b = this.readBuffer;
|
|
||||||
b.position = 0;
|
|
||||||
|
|
||||||
if (b.length <= 8) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg_len:Number = parseInt(b.readUTFBytes(8), 10);
|
|
||||||
if (b.length >= msg_len + 8) {
|
|
||||||
var bytes:String = b.readUTFBytes(msg_len);
|
|
||||||
var data:Object = vegas.strings.JSON.deserialize(bytes);
|
|
||||||
var t:ByteArray = new ByteArray();
|
|
||||||
t.writeBytes(b, msg_len + 8);
|
|
||||||
this.readBuffer = t;
|
|
||||||
|
|
||||||
// Send the message to all clients.
|
|
||||||
for (var client:String in this.clients) {
|
|
||||||
var subscribed = false;
|
|
||||||
|
|
||||||
for (var i:String in data.subscribers) {
|
|
||||||
var phid = data.subscribers[i];
|
|
||||||
|
|
||||||
if (this.subscriptions[phid] &&
|
|
||||||
this.subscriptions[phid][client]) {
|
|
||||||
subscribed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subscribed) {
|
|
||||||
this.log('Sending message to client: ' + client);
|
|
||||||
this.send.send(client, 'receiveMessage', data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
} catch (err:Error) {
|
|
||||||
this.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setStatusOnClients(
|
|
||||||
status:String,
|
|
||||||
code:String = null):void {
|
|
||||||
|
|
||||||
this.status = status;
|
|
||||||
this.statusCode = code;
|
|
||||||
|
|
||||||
for (var client:String in this.clients) {
|
|
||||||
this.send.send(client, 'setStatus', status, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +1,9 @@
|
||||||
/**
|
|
||||||
* Notification server. Launch with:
|
|
||||||
*
|
|
||||||
* sudo node aphlict_server.js --user=aphlict
|
|
||||||
*
|
|
||||||
* You can also specify `port`, `admin`, `host` and `log`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var JX = require('./lib/javelin').JX;
|
var JX = require('./lib/javelin').JX;
|
||||||
|
var http = require('http');
|
||||||
|
var https = require('https');
|
||||||
|
var util = require('util');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
JX.require('lib/AphlictFlashPolicyServer', __dirname);
|
|
||||||
JX.require('lib/AphlictListenerList', __dirname);
|
JX.require('lib/AphlictListenerList', __dirname);
|
||||||
JX.require('lib/AphlictLog', __dirname);
|
JX.require('lib/AphlictLog', __dirname);
|
||||||
|
|
||||||
|
@ -17,8 +12,10 @@ function parse_command_line_arguments(argv) {
|
||||||
port: 22280,
|
port: 22280,
|
||||||
admin: 22281,
|
admin: 22281,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
user: null,
|
log: '/var/log/aphlict.log',
|
||||||
log: '/var/log/aphlict.log'
|
'ssl-key': null,
|
||||||
|
'ssl-certificate': null,
|
||||||
|
test: false
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var ii = 2; ii < argv.length; ii++) {
|
for (var ii = 2; ii < argv.length; ii++) {
|
||||||
|
@ -42,124 +39,120 @@ function parse_command_line_arguments(argv) {
|
||||||
var debug = new JX.AphlictLog()
|
var debug = new JX.AphlictLog()
|
||||||
.addConsole(console);
|
.addConsole(console);
|
||||||
|
|
||||||
var clients = new JX.AphlictListenerList();
|
|
||||||
|
|
||||||
var config = parse_command_line_arguments(process.argv);
|
var config = parse_command_line_arguments(process.argv);
|
||||||
|
|
||||||
|
process.on('uncaughtException', function(err) {
|
||||||
|
debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
var WebSocket;
|
||||||
|
try {
|
||||||
|
WebSocket = require('ws');
|
||||||
|
} catch (ex) {
|
||||||
|
throw new Error(
|
||||||
|
'You need to install the Node.js "ws" module for websocket support. ' +
|
||||||
|
'Usually, you can do this with `npm install -g ws`. ' + ex.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
var ssl_config = {
|
||||||
|
enabled: (config['ssl-key'] || config['ssl-cert'])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the SSL certificates (if any were provided) now, so that runs with
|
||||||
|
// `--test` will see any errors.
|
||||||
|
if (ssl_config.enabled) {
|
||||||
|
ssl_config.key = fs.readFileSync(config['ssl-key']);
|
||||||
|
ssl_config.cert = fs.readFileSync(config['ssl-cert']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the logfile so we'll fail if we can't write to it.
|
||||||
if (config.logfile) {
|
if (config.logfile) {
|
||||||
debug.addLogfile(config.logfile);
|
debug.addLogfile(config.logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.getuid() !== 0) {
|
// If we're just doing a configuration test, exit here before starting any
|
||||||
console.log(
|
// servers.
|
||||||
"ERROR: " +
|
if (config.test) {
|
||||||
"This server must be run as root because it needs to bind to privileged " +
|
debug.log('Configuration test OK.');
|
||||||
"port 843 to start a Flash policy server. It will downgrade to run as a " +
|
process.exit(0);
|
||||||
"less-privileged user after binding if you pass a user in the command " +
|
|
||||||
"line arguments with '--user=alincoln'.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var net = require('net');
|
var start_time = new Date().getTime();
|
||||||
var http = require('http');
|
var messages_out = 0;
|
||||||
|
var messages_in = 0;
|
||||||
|
|
||||||
process.on('uncaughtException', function(err) {
|
var clients = new JX.AphlictListenerList();
|
||||||
debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack);
|
|
||||||
|
|
||||||
process.exit(1);
|
function https_discard_handler(req, res) {
|
||||||
});
|
res.writeHead(501);
|
||||||
|
res.end('HTTP/501 Use Websockets\n');
|
||||||
|
}
|
||||||
|
|
||||||
new JX.AphlictFlashPolicyServer()
|
var ws;
|
||||||
.setDebugLog(debug)
|
if (ssl_config.enabled) {
|
||||||
.setAccessPort(config.port)
|
var https_server = https.createServer({
|
||||||
.start();
|
key: ssl_config.key,
|
||||||
|
cert: ssl_config.cert
|
||||||
|
}, https_discard_handler).listen(config.port);
|
||||||
|
|
||||||
|
ws = new WebSocket.Server({server: https_server});
|
||||||
|
} else {
|
||||||
|
ws = new WebSocket.Server({port: config.port});
|
||||||
|
}
|
||||||
|
|
||||||
net.createServer(function(socket) {
|
ws.on('connection', function(ws) {
|
||||||
var listener = clients.addListener(socket);
|
var listener = clients.addListener(ws);
|
||||||
|
|
||||||
debug.log('<%s> Connected from %s',
|
function log() {
|
||||||
listener.getDescription(),
|
debug.log(
|
||||||
socket.remoteAddress);
|
util.format('<%s>', listener.getDescription()) +
|
||||||
|
' ' +
|
||||||
|
util.format.apply(null, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
var buffer = new Buffer([]);
|
log('Connected from %s.', ws._socket.remoteAddress);
|
||||||
var length = 0;
|
|
||||||
|
|
||||||
socket.on('data', function(data) {
|
ws.on('message', function(data) {
|
||||||
buffer = Buffer.concat([buffer, new Buffer(data)]);
|
log('Received message: %s', data);
|
||||||
|
|
||||||
while (buffer.length) {
|
var message;
|
||||||
if (!length) {
|
try {
|
||||||
length = buffer.readUInt16BE(0);
|
message = JSON.parse(data);
|
||||||
buffer = buffer.slice(2);
|
} catch (err) {
|
||||||
}
|
log('Message is invalid: %s', err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer.length < length) {
|
switch (message.command) {
|
||||||
// We need to wait for the rest of the data.
|
case 'subscribe':
|
||||||
return;
|
log(
|
||||||
}
|
'Subscribed to: %s',
|
||||||
|
JSON.stringify(message.data));
|
||||||
|
listener.subscribe(message.data);
|
||||||
|
break;
|
||||||
|
|
||||||
var message;
|
case 'unsubscribe':
|
||||||
try {
|
log(
|
||||||
message = JSON.parse(buffer.toString('utf8', 0, length));
|
'Unsubscribed from: %s',
|
||||||
} catch (err) {
|
JSON.stringify(message.data));
|
||||||
debug.log('<%s> Received invalid data.', listener.getDescription());
|
listener.unsubscribe(message.data);
|
||||||
continue;
|
break;
|
||||||
} finally {
|
|
||||||
buffer = buffer.slice(length);
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.log('<%s> Received data: %s',
|
default:
|
||||||
listener.getDescription(),
|
log('Unrecognized command "%s".', message.command || '<undefined>');
|
||||||
JSON.stringify(message));
|
|
||||||
|
|
||||||
switch (message.command) {
|
|
||||||
case 'subscribe':
|
|
||||||
debug.log(
|
|
||||||
'<%s> Subscribed to: %s',
|
|
||||||
listener.getDescription(),
|
|
||||||
JSON.stringify(message.data));
|
|
||||||
listener.subscribe(message.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unsubscribe':
|
|
||||||
debug.log(
|
|
||||||
'<%s> Unsubscribed from: %s',
|
|
||||||
listener.getDescription(),
|
|
||||||
JSON.stringify(message.data));
|
|
||||||
listener.unsubscribe(message.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
debug.log('<s> Unrecognized command.', listener.getDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('close', function() {
|
ws.on('close', function() {
|
||||||
clients.removeListener(listener);
|
clients.removeListener(listener);
|
||||||
debug.log('<%s> Disconnected', listener.getDescription());
|
log('Disconnected.');
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('timeout', function() {
|
ws.on('error', function(err) {
|
||||||
debug.log('<%s> Timed Out', listener.getDescription());
|
log('Error: %s', err.message);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
socket.on('end', function() {
|
|
||||||
debug.log('<%s> Ended Connection', listener.getDescription());
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('error', function(e) {
|
|
||||||
debug.log('<%s> Error: %s', listener.getDescription(), e);
|
|
||||||
});
|
|
||||||
|
|
||||||
}).listen(config.port);
|
|
||||||
|
|
||||||
|
|
||||||
var messages_out = 0;
|
|
||||||
var messages_in = 0;
|
|
||||||
var start_time = new Date().getTime();
|
|
||||||
|
|
||||||
function transmit(msg) {
|
function transmit(msg) {
|
||||||
var listeners = clients.getListeners().filter(function(client) {
|
var listeners = clients.getListeners().filter(function(client) {
|
||||||
|
@ -195,7 +188,7 @@ http.createServer(function(request, response) {
|
||||||
try {
|
try {
|
||||||
var msg = JSON.parse(body);
|
var msg = JSON.parse(body);
|
||||||
|
|
||||||
debug.log('notification: ' + JSON.stringify(msg));
|
debug.log('Received notification: ' + JSON.stringify(msg));
|
||||||
++messages_in;
|
++messages_in;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -242,10 +235,4 @@ http.createServer(function(request, response) {
|
||||||
}
|
}
|
||||||
}).listen(config.admin, config.host);
|
}).listen(config.admin, config.host);
|
||||||
|
|
||||||
// If we're configured to drop permissions, get rid of them now that we've
|
|
||||||
// bound to the ports we need and opened logfiles.
|
|
||||||
if (config.user) {
|
|
||||||
process.setuid(config.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.log('Started Server (PID %d)', process.pid);
|
debug.log('Started Server (PID %d)', process.pid);
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
var JX = require('javelin').JX;
|
|
||||||
|
|
||||||
var net = require('net');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server which handles cross-domain policy requests for Flash.
|
|
||||||
*
|
|
||||||
* var server = new AphlictFlashPolicyServer()
|
|
||||||
* .setAccessPort(9999)
|
|
||||||
* .start();
|
|
||||||
*/
|
|
||||||
JX.install('AphlictFlashPolicyServer', {
|
|
||||||
|
|
||||||
members: {
|
|
||||||
_server: null,
|
|
||||||
_port: 843,
|
|
||||||
_accessPort: null,
|
|
||||||
_debug: null,
|
|
||||||
|
|
||||||
setDebugLog: function(log) {
|
|
||||||
this._debug = log;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
setAccessPort: function(port) {
|
|
||||||
this._accessPort = port;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this._server = net.createServer(JX.bind(this, this._didConnect));
|
|
||||||
this._server.listen(this._port);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
_didConnect: function(socket) {
|
|
||||||
this._log('<FlashPolicy> Policy Request From %s', socket.remoteAddress);
|
|
||||||
|
|
||||||
socket.on('error', JX.bind(this, this._didSocketError, socket));
|
|
||||||
|
|
||||||
socket.write(this._getFlashPolicyResponse());
|
|
||||||
socket.end();
|
|
||||||
},
|
|
||||||
|
|
||||||
_didSocketError: function(socket, error) {
|
|
||||||
this._log('<FlashPolicy> Socket Error: %s', error);
|
|
||||||
},
|
|
||||||
|
|
||||||
_log: function() {
|
|
||||||
this._debug && this._debug.log.apply(this._debug, arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
_getFlashPolicyResponse: function() {
|
|
||||||
var policy = [
|
|
||||||
'<?xml version="1.0"?>',
|
|
||||||
'<!DOCTYPE cross-domain-policy SYSTEM ' +
|
|
||||||
'"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">',
|
|
||||||
'<cross-domain-policy>',
|
|
||||||
'<allow-access-from domain="*" to-ports="' + this._accessPort + '"/>',
|
|
||||||
'</cross-domain-policy>'
|
|
||||||
];
|
|
||||||
|
|
||||||
return policy.join('\n') + '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
|
@ -49,15 +49,7 @@ JX.install('AphlictListener', {
|
||||||
},
|
},
|
||||||
|
|
||||||
writeMessage: function(message) {
|
writeMessage: function(message) {
|
||||||
var serial = JSON.stringify(message);
|
this._socket.send(JSON.stringify(message));
|
||||||
|
|
||||||
var length = Buffer.byteLength(serial, 'utf8');
|
|
||||||
length = length.toString();
|
|
||||||
while (length.length < 8) {
|
|
||||||
length = '0' + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._socket.write(length + serial);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,39 +2,31 @@
|
||||||
* @provides javelin-aphlict
|
* @provides javelin-aphlict
|
||||||
* @requires javelin-install
|
* @requires javelin-install
|
||||||
* javelin-util
|
* javelin-util
|
||||||
|
* javelin-websocket
|
||||||
|
* javelin-leader
|
||||||
|
* javelin-json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JS API for the Flash Aphlict client. Example usage:
|
* Client for the notification server. Example usage:
|
||||||
*
|
*
|
||||||
* var aphlict = new JX.Aphlict('aphlict_swf', '127.0.0.1', 22280)
|
* var aphlict = new JX.Aphlict('ws://localhost:22280', subscriptions)
|
||||||
* .setHandler(function(type, message) {
|
* .setHandler(function(message) {
|
||||||
* JX.log("Got " + type + " event!")
|
* // ...
|
||||||
* })
|
* })
|
||||||
* .start();
|
* .start();
|
||||||
*
|
*
|
||||||
* Your handler will receive these events:
|
|
||||||
*
|
|
||||||
* - `connect` The client initiated a connection to the server.
|
|
||||||
* - `connected` The client completed a connection to the server.
|
|
||||||
* - `close` The client disconnected from the server.
|
|
||||||
* - `error` There was an error.
|
|
||||||
* - `receive` Received a message from the server.
|
|
||||||
*
|
|
||||||
* You do not have to handle any of them in any specific way.
|
|
||||||
*/
|
*/
|
||||||
JX.install('Aphlict', {
|
JX.install('Aphlict', {
|
||||||
|
|
||||||
construct: function(id, server, port, subscriptions) {
|
construct: function(uri, subscriptions) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
if (JX.Aphlict._instance) {
|
if (JX.Aphlict._instance) {
|
||||||
JX.$E('Aphlict object is a singleton.');
|
JX.$E('Aphlict object is a singleton.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._id = id;
|
this._uri = uri;
|
||||||
this._server = server;
|
|
||||||
this._port = port;
|
|
||||||
this._subscriptions = subscriptions;
|
this._subscriptions = subscriptions;
|
||||||
this._setStatus('setup');
|
this._setStatus('setup');
|
||||||
|
|
||||||
|
@ -44,7 +36,6 @@ JX.install('Aphlict', {
|
||||||
events: ['didChangeStatus'],
|
events: ['didChangeStatus'],
|
||||||
|
|
||||||
members: {
|
members: {
|
||||||
_id: null,
|
|
||||||
_server: null,
|
_server: null,
|
||||||
_port: null,
|
_port: null,
|
||||||
_subscriptions: null,
|
_subscriptions: null,
|
||||||
|
@ -52,47 +43,92 @@ JX.install('Aphlict', {
|
||||||
_statusCode: null,
|
_statusCode: null,
|
||||||
|
|
||||||
start: function(node, uri) {
|
start: function(node, uri) {
|
||||||
this._setStatus('start');
|
JX.Leader.listen('onBecomeLeader', JX.bind(this, this._lead));
|
||||||
|
JX.Leader.listen('onReceiveBroadcast', JX.bind(this, this._receive));
|
||||||
|
JX.Leader.start();
|
||||||
|
|
||||||
// NOTE: This is grotesque, but seems to work everywhere.
|
JX.Leader.call(JX.bind(this, this._begin));
|
||||||
node.innerHTML =
|
|
||||||
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">' +
|
|
||||||
'<param name="movie" value="' + uri + '" />' +
|
|
||||||
'<param name="allowScriptAccess" value="always" />' +
|
|
||||||
'<param name="wmode" value="opaque" />' +
|
|
||||||
'<embed src="' + uri + '" wmode="opaque"' +
|
|
||||||
'width="0" height="0" id="' + this._id + '">' +
|
|
||||||
'</embed>' +
|
|
||||||
'</object>';
|
|
||||||
},
|
|
||||||
|
|
||||||
_didStartFlash: function() {
|
|
||||||
var id = this._id;
|
|
||||||
|
|
||||||
// Flash puts its "objects" into global scope in an inconsistent way,
|
|
||||||
// because it was written in like 1816 when globals were awesome and IE4
|
|
||||||
// didn't support other scopes since global scope is the best anyway.
|
|
||||||
var container = document[id] || window[id];
|
|
||||||
|
|
||||||
this._flashContainer = container;
|
|
||||||
this._flashContainer.connect(
|
|
||||||
this._server,
|
|
||||||
this._port,
|
|
||||||
this._subscriptions);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getStatus: function() {
|
getStatus: function() {
|
||||||
return this._status;
|
return this._status;
|
||||||
},
|
},
|
||||||
|
|
||||||
getStatusCode: function() {
|
_begin: function() {
|
||||||
return this._statusCode;
|
JX.Leader.broadcast(
|
||||||
|
null,
|
||||||
|
{type: 'aphlict.getstatus'});
|
||||||
|
JX.Leader.broadcast(
|
||||||
|
null,
|
||||||
|
{type: 'aphlict.subscribe', data: this._subscriptions});
|
||||||
},
|
},
|
||||||
|
|
||||||
_setStatus: function(status, code) {
|
_lead: function() {
|
||||||
|
var socket = new JX.WebSocket(this._uri);
|
||||||
|
socket.setOpenHandler(JX.bind(this, this._open));
|
||||||
|
socket.setMessageHandler(JX.bind(this, this._message));
|
||||||
|
socket.setCloseHandler(JX.bind(this, this._close));
|
||||||
|
|
||||||
|
this._socket = socket;
|
||||||
|
|
||||||
|
socket.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
_open: function() {
|
||||||
|
this._broadcastStatus('open');
|
||||||
|
JX.Leader.broadcast(null, {type: 'aphlict.getsubscribers'});
|
||||||
|
},
|
||||||
|
|
||||||
|
_close: function() {
|
||||||
|
this._broadcastStatus('closed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_broadcastStatus: function(status) {
|
||||||
|
JX.Leader.broadcast(null, {type: 'aphlict.status', data: status});
|
||||||
|
},
|
||||||
|
|
||||||
|
_message: function(raw) {
|
||||||
|
var message = JX.JSON.parse(raw);
|
||||||
|
JX.Leader.broadcast(null, {type: 'aphlict.server', data: message});
|
||||||
|
},
|
||||||
|
|
||||||
|
_receive: function(message, is_leader) {
|
||||||
|
switch (message.type) {
|
||||||
|
case 'aphlict.status':
|
||||||
|
this._setStatus(message.data);
|
||||||
|
break;
|
||||||
|
case 'aphlict.getstatus':
|
||||||
|
if (is_leader) {
|
||||||
|
this._broadcastStatus(this.getStatus());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'aphlict.getsubscribers':
|
||||||
|
JX.Leader.broadcast(
|
||||||
|
null,
|
||||||
|
{type: 'aphlict.subscribe', data: this._subscriptions});
|
||||||
|
break;
|
||||||
|
case 'aphlict.subscribe':
|
||||||
|
if (is_leader) {
|
||||||
|
this._write({
|
||||||
|
command: 'subscribe',
|
||||||
|
data: message.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'aphlict.server':
|
||||||
|
var handler = this.getHandler();
|
||||||
|
handler && handler(message.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_setStatus: function(status) {
|
||||||
this._status = status;
|
this._status = status;
|
||||||
this._statusCode = code || null;
|
|
||||||
this.invoke('didChangeStatus');
|
this.invoke('didChangeStatus');
|
||||||
|
},
|
||||||
|
|
||||||
|
_write: function(message) {
|
||||||
|
this._socket.send(JX.JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -110,28 +146,8 @@ JX.install('Aphlict', {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return self._instance;
|
return self._instance;
|
||||||
},
|
|
||||||
|
|
||||||
didReceiveEvent: function(type, message) {
|
|
||||||
var client = JX.Aphlict.getInstance();
|
|
||||||
if (!client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == 'status') {
|
|
||||||
client._setStatus(message.type, message.code);
|
|
||||||
switch (message.type) {
|
|
||||||
case 'ready':
|
|
||||||
client._didStartFlash();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = client.getHandler();
|
|
||||||
if (handler) {
|
|
||||||
handler(type, message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,22 +41,8 @@ JX.behavior('aphlict-listen', function(config) {
|
||||||
|
|
||||||
// Respond to a notification from the Aphlict notification server. We send
|
// Respond to a notification from the Aphlict notification server. We send
|
||||||
// a request to Phabricator to get notification details.
|
// a request to Phabricator to get notification details.
|
||||||
function onaphlictmessage(type, message) {
|
function onaphlictmessage(message) {
|
||||||
switch (type) {
|
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
||||||
case 'receive':
|
|
||||||
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
case 'error':
|
|
||||||
case 'log':
|
|
||||||
case 'status':
|
|
||||||
if (config.debug) {
|
|
||||||
var details = message ? JX.JSON.stringify(message) : '';
|
|
||||||
JX.log('(Aphlict) [' + type + '] ' + details);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,13 +75,11 @@ JX.behavior('aphlict-listen', function(config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = new JX.Aphlict(
|
var client = new JX.Aphlict(
|
||||||
config.id,
|
config.websocketURI,
|
||||||
config.server,
|
|
||||||
config.port,
|
|
||||||
config.subscriptions);
|
config.subscriptions);
|
||||||
|
|
||||||
client
|
client
|
||||||
.setHandler(onaphlictmessage)
|
.setHandler(onaphlictmessage)
|
||||||
.start(JX.$(config.containerID), config.swfURI);
|
.start();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue