summaryrefslogtreecommitdiff
path: root/src/mmsrip
diff options
context:
space:
mode:
Diffstat (limited to 'src/mmsrip')
-rw-r--r--src/mmsrip/AUTHORS27
-rw-r--r--src/mmsrip/COPYING340
-rw-r--r--src/mmsrip/ChangeLog156
-rw-r--r--src/mmsrip/NEWS68
-rw-r--r--src/mmsrip/README37
-rw-r--r--src/mmsrip/common.h50
-rw-r--r--src/mmsrip/error.c117
-rw-r--r--src/mmsrip/error.h40
-rw-r--r--src/mmsrip/main.c753
-rw-r--r--src/mmsrip/mms.c1239
-rw-r--r--src/mmsrip/mms.h129
11 files changed, 2956 insertions, 0 deletions
diff --git a/src/mmsrip/AUTHORS b/src/mmsrip/AUTHORS
new file mode 100644
index 0000000..530a73f
--- /dev/null
+++ b/src/mmsrip/AUTHORS
@@ -0,0 +1,27 @@
+Nicolas BENOIT (main developper)
+nbenoit@tuxfamily.org
+http://nbenoit.tuxfamily.org
+
+Major MMS (author of mmsclient)
+http://www.geocities.com/majormms
+
+SDP Multimedia (author of the only doc about MMS)
+http://get.to/sdp
+
+Aurelien REQUIEM (author of mmsrip's ebuild script)
+aurelien@menfin.net
+
+Luis COSTA (initial idea and patch for the -d/--delay option)
+izhirahider@gmail.com
+
+Jeff FULMER (Solaris port)
+jeff@joedog.org
+
+Federico SIMONCELLI (RPMs for Fedora Core 4)
+federico.simoncelli@gmail.com
+
+Kyuzz (Fix for Cygwin compilation)
+kyuzz.org
+
+Xavier ROCHE (Initial port to Win32)
+roche@httrack.com
diff --git a/src/mmsrip/COPYING b/src/mmsrip/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/src/mmsrip/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/mmsrip/ChangeLog b/src/mmsrip/ChangeLog
new file mode 100644
index 0000000..da17d27
--- /dev/null
+++ b/src/mmsrip/ChangeLog
@@ -0,0 +1,156 @@
+2006-01-24 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * README, NEWS: getting ready for 0.7.0 release.
+ * gentoo/mmsrip-0.7.0.ebuild: ebuild script for 0.7.0 release.
+ * spec/mmsrip.spec: updated for 0.7.0 release.
+
+
+2006-01-23 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.h, src/mms.c, src/main.c, doc/mmsrip.1: renamed compatibility mode to trick enabled mode (-c/--compat switches are now -k/--trick).
+ * src/mms.c, src/main.c: fixed parsing of urls that look like 'stream.asf?digest=7Q2bjXo&provider=lala'.
+ * src/mms.c, src/mms.h, src/main.c, doc/mmsrip.1, configure.ac: replaced configure's --enable-debug switch by a -gFILE/--debug=FILE runtime switch.
+ * doc/mmsrip.1: improved presentation.
+
+
+2006-01-22 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.c: fixed presentation in debug output.
+ * AUTHORS: updated.
+ * src/mms.c: improved debug ouput for header interpretation.
+ * src/mms.c: fixed expected file size.
+ * src/mms.c: added a few calls to mms_get_32() for code lisibility.
+ * src/mms.c: added mms_get_64() function (improves lisibility in mms_interp_header()).
+ * src/mms.c: improved ASF header interpretation.
+ * src/mms.h, src/mms.c, src/main.c, doc/mmsrip.1: added compatibility mode and -c/--compat switches.
+ * src/main.c: minor presentation improvements.
+ * src/mms.c: added an entry for mmsh:// protocol though we don't support it.
+
+
+2006-01-21 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.h, src/mms.c, src/error.c, src/error.h, src/main.c, configure.ac: fixed compilation on Win32 (thanks to Xavier ROCHE for the initial port).
+ * AUTHORS: updated.
+ * src/mms.c: fixed URL parsing (mmst:// is now ok).
+ * src/mms.c, doc/mmsrip.1: the user should read the manpage when the server sends a no auth error.
+ * configure.ac: added a switch to enable debug output to stdout.
+
+
+2006-01-20 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.h, src/main.c, configure.ac: fixed compilation on Solaris (from Jeff FULMER's Solaris port).
+ * AUTHORS: updated.
+ * gentoo/mmsrip-0.6.6.ebuild: ebuild script for 0.6.6 release.
+ * spec/mmsrip.spec: spec file for RPM building (thanks to Federico SIMONCELLI).
+ * src/mms.c, configure.ac: added the display of the ripping speed.
+
+
+2006-01-17 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.h: fixed compilation on Cygwin (greetings to Kyuzz for bug reporting).
+
+
+2006-01-06 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.6.5 release.
+
+
+2006-01-06 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.c: fixed a call to error().
+ * src/mms.h: reordered some error codes.
+ * src/main.c: improved the error code returned by the program.
+ * doc/mmsrip.1: added some documentation about the program's returned value.
+ * gentoo/mmsrip-0.6.5.ebuild: ebuild script for 0.6.5 release.
+ * README, NEWS: getting ready for 0.6.5 release.
+
+
+2005-11-23 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.h, src/mms.c, src/main.c: replaced a few values with #defines.
+ * src/mms.c: added the support for no auth errors (this happens sometimes, for example on canalplus.fr).
+
+
+2005-07-09 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.6.4 release.
+
+
+2005-07-09 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * doc/mmsrip.1: added man page.
+ * doc/Makefile.am, Makefile.am, configure.ac: added the man page to the package.
+ * src/main.c: fixed a compilation warning about variable `end` initialization.
+ * src/mms.c: fixed a potential security issue in the handling of files containing more than 20 streams (patch ported from MPlayer).
+ * src/mms.c: fixed a bug in the media stream MBR selection that prevented ASF files from being ripped properly (bug reported by Jozef RIHA).
+ * gentoo/mmsrip-0.6.4.ebuild: ebuild script for 0.6.4 release.
+ * README, NEWS: getting ready for 0.6.4 release.
+
+
+2005-06-05 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.6.2 release.
+
+
+2005-06-05 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * Makefile.am: ebuild script moved in a dedicated gentoo directory.
+ * gentoo/mmsrip-0.6.2.ebuild: ebuild script for 0.6.2 release.
+ * src/main.c: added '-d' switch which makes mmsrip exit after the specified delay (idea and patch by Luis COSTA).
+ * src/main.c: fixed bug that made mmsrip attempt to use invalid URLs.
+ * AUTHORS: updated.
+ * src/main.c: added support for long options.
+ * README, NEWS: getting ready for 0.6.2 release.
+
+
+2005-05-29 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac: 0.6.0 release.
+
+
+2005-05-29 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/main.c: added '-q' switch which makes mmsrip quiet.
+ * src/mms.c, src/mms.h: added support for quiet mode.
+ * src/main.c: added '-t' switch which makes mmsrip check stream availability only.
+ * src/main.c: added '-o' switch which makes mmsrip output stream to specified file.
+ * src/main.c: fixed a bug in args handling.
+ * README, NEWS, src/common.h: getting ready for 0.6.0 release.
+
+
+2005-05-28 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * mmsrip-0.6.0.ebuild: added Aurelien REQUIEM's ebuild.
+ * README: updated.
+ * AUTHORS: updated.
+ * Makefile.am: added ebuild script to EXTRA_DIST.
+ * configure.ac: added CVS infos.
+
+
+2005-02-21 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.4.2 release.
+
+
+2005-02-21 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/mms.c: Removed bad EOF handling in mms_recv_packet().
+ * README, NEWS: Getting ready for 0.4.2.
+
+
+2005-02-20 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.4.1 release.
+
+
+2005-02-20 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * src/main.c: Fixed bug with multiple streams (mmsrip used to download the same stream every time).
+ * configure.ac: Removed unnecessary checks.
+ * src/mms.c: Removed strndup() because a lot of people don't have it.
+ * README, NEWS: Getting ready for 0.4.1.
+
+
+2005-02-20 Nicolas BENOIT <nbenoit@tuxfamily.org>
+
+ * configure.ac, src/common.h: 0.4.0 release.
diff --git a/src/mmsrip/NEWS b/src/mmsrip/NEWS
new file mode 100644
index 0000000..43b54aa
--- /dev/null
+++ b/src/mmsrip/NEWS
@@ -0,0 +1,68 @@
+ MMS Ripper v0.7.0:
+====================
+
+ - Added '-k' and '--trick' switches that activate a trick for some recalcitrant servers.
+ - Added the display of the ripping speed.
+ - Added '-gFILE' and '--debug=FILE' switches which activate debug output to specified file.
+ - Fixed parsing of urls that include options after file name (stream.asf?dig=7QXo&pr=lala).
+ - Fixed protocol checking in URL parsing.
+ - Fixed expected file size calculation.
+ - Fixed compilation on Solaris, Cygwin and Win32 (check AUTHORS file for greetings).
+ - Improved debug output.
+ - Improved ASF header interpretation.
+ - Improved presentation in manpage.
+ - Minor code cleanups.
+ - A spec file is now distributed.
+
+
+ MMS Ripper v0.6.5:
+====================
+
+ - Added the handling of the no auth error during streaming.
+ (this should fix mmsrip behavior on some servers such as canalplus.fr)
+ - Improved the diagnostic value returned by mmsrip in case of error (please read the manpage).
+
+
+ MMS Ripper v0.6.4:
+====================
+
+ - Added a man page.
+ - Fixed a compilation warning.
+ - Fixed a potential security issue.
+ - Fixed the ripping of some ASF files (bug reported by Jozef RIHA).
+
+
+ MMS Ripper v0.6.2:
+====================
+
+ - Added '-d' switch which makes mmsrip exit after DELAY seconds.
+ - Added support for long options.
+ - Fixed bug that made mmsrip attempt to use invalid URLs.
+
+
+ MMS Ripper v0.6.0:
+====================
+
+ - Added '-q' switch which enables quiet mode.
+ - Added '-t' switch which makes mmsrip check stream availability only.
+ - Added '-o' switch so the user can choose the output file for every stream.
+ - Fixed a bug in arguments handling.
+
+
+ MMS Ripper v0.4.2:
+====================
+
+ - Fixed a serious bug in packet reception (mmsrip went crazy).
+
+
+ MMS Ripper v0.4.1:
+====================
+
+ - Fixed a bug that made mmsrip download the same stream every time.
+ - Compilation fixes.
+
+
+ MMS Ripper v0.4.0:
+====================
+
+ - First Release of mmsrip.
diff --git a/src/mmsrip/README b/src/mmsrip/README
new file mode 100644
index 0000000..bf7f7d1
--- /dev/null
+++ b/src/mmsrip/README
@@ -0,0 +1,37 @@
+ MMS Ripper release 0.7.0
+
+These are the release notes for mmsrip version 0.7.0
+Read them carefully, as they tell you what this is all about.
+
+
+WHAT IS MMSRIP ?
+
+ MMSRIP allows you to save on your hard-disk the content being streamed
+ by an MMS server. This program has been written for personnal use, so
+ don't blame me if you think I am stupid doing such tool for the others.
+
+ It should run on every POSIX compliant Operating System, but I can't
+ give you any complete list.
+
+
+HOW TO INSTALL ?
+
+ Read the file INSTALL in order to get the answer of this question... ;)
+ If you use a gentoo based distribution, enjoy the ebuild script contributed by Aurelien REQUIEM.
+ If you use a RPM based distribution, you may use the spec file provided by Frederico SIMONCELLI.
+
+
+HOW TO RUN ?
+
+ Once you have compiled & installed, you should be able to run the program.
+
+
+IMPORTANT NOTE
+
+ All the credits go to SDP Multimedia and Major MMS.
+
+
+--
+
+$RCSfile: README,v $
+$Date: 2006/01/24 18:13:07 $ - $Revision: 1.13 $
diff --git a/src/mmsrip/common.h b/src/mmsrip/common.h
new file mode 100644
index 0000000..47f5567
--- /dev/null
+++ b/src/mmsrip/common.h
@@ -0,0 +1,50 @@
+/*
+ * $RCSfile: common.h,v $
+ * $Date: 2006/01/17 19:59:27 $ - $Revision: 1.11 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define PROGRAM_SHORT_NAME "mmsrip"
+#define PROGRAM_FULL_NAME "MMS Ripper"
+#define PROGRAM_VERSION "0.7.0-rc1"
+#endif
+
+#define PROGRAM_AUTHORS "Nicolas BENOIT <nbenoit@tuxfamily.org>"
+#define PROGRAM_HELPERS "SDP Multimedia and Major MMS"
+
+
+/*
+ * quick struct in order to make list of streams
+ */
+typedef struct
+{
+ void *next;
+ char *stream;
+ char *output;
+} STREAM_LIST;
+
+#endif
diff --git a/src/mmsrip/error.c b/src/mmsrip/error.c
new file mode 100644
index 0000000..9d2f4c9
--- /dev/null
+++ b/src/mmsrip/error.c
@@ -0,0 +1,117 @@
+/*
+ * $RCSfile: error.c,v $
+ * $Date: 2006/01/21 20:09:57 $ - $Revision: 1.7 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+#include "error.h"
+
+
+/*
+ * Prints A Warning
+ */
+#ifdef HAVE_VSNPRINTF
+void
+warning ( const char *prod,
+ const char *format,
+ ... )
+{
+ char *message;
+ va_list ap;
+
+ if ( format == NULL )
+ return;
+
+ message = ( char * ) malloc ( ERROR_MSG_LEN );
+ va_start ( ap, format );
+ vsnprintf ( message, ERROR_MSG_LEN, format, ap );
+ va_end ( ap );
+
+ if ( prod != NULL )
+ fprintf ( stderr, "warning in %s(): %s.\n", prod, message );
+ else
+ fprintf ( stderr, "warning: %s.\n", message );
+
+ free ( message );
+}
+#else
+void
+warning ( const char *prod,
+ const char *message )
+{
+ if ( message == NULL )
+ return;
+
+ if ( prod != NULL )
+ fprintf ( stderr, "warning in %s(): %s.\n", prod, message );
+ else
+ fprintf ( stderr, "warning: %s.\n", message );
+}
+#endif
+
+
+/*
+ * Prints An Error Message
+ */
+#ifdef HAVE_VSNPRINTF
+void
+error ( const char *prod,
+ const char *format,
+ ... )
+{
+ char *message;
+ va_list ap;
+
+ if ( format == NULL )
+ return;
+
+ message = ( char * ) malloc ( ERROR_MSG_LEN );
+ va_start ( ap, format );
+ vsnprintf ( message, ERROR_MSG_LEN, format, ap );
+ va_end ( ap );
+
+ if ( prod != NULL )
+ fprintf ( stderr, "error in %s(): %s.\n", prod, message );
+ else
+ fprintf ( stderr, "error: %s.\n", message );
+
+ free ( message );
+}
+#else
+void
+error ( const char *prod,
+ const char *message )
+{
+ if ( message == NULL )
+ return;
+
+ if ( prod != NULL )
+ fprintf ( stderr, "error in %s(): %s.\n", prod, message );
+ else
+ fprintf ( stderr, "error: %s.\n", message );
+}
+#endif
diff --git a/src/mmsrip/error.h b/src/mmsrip/error.h
new file mode 100644
index 0000000..d0e379c
--- /dev/null
+++ b/src/mmsrip/error.h
@@ -0,0 +1,40 @@
+/*
+ * $RCSfile: error.h,v $
+ * $Date: 2006/01/21 20:09:57 $ - $Revision: 1.4 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+#ifdef HAVE_VSNPRINTF
+#include <stdarg.h>
+
+#define ERROR_MSG_LEN 128
+
+void warning ( const char *, const char *, ... );
+void error ( const char *, const char *, ... );
+#else
+void warning ( const char *, const char * );
+void error ( const char *, const char * );
+#endif
+
+#endif
diff --git a/src/mmsrip/main.c b/src/mmsrip/main.c
new file mode 100644
index 0000000..a575b4a
--- /dev/null
+++ b/src/mmsrip/main.c
@@ -0,0 +1,753 @@
+/*
+ * $RCSfile: main.c,v $
+ * $Date: 2006/01/23 20:30:42 $ - $Revision: 1.32 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include "common.h"
+#include "mms.h"
+#include "error.h"
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+/*
+ * options
+ */
+#if defined(SOLARIS) || defined(sun)
+static char * options = "ahvtqko:d:g:";
+#else
+static char * options = "-ahvtqko:d:g:";
+#endif
+
+#ifdef HAVE_GETOPT_LONG
+static struct option long_options[] = {
+ {"about", 0, NULL, 'a'},
+ {"version", 0, NULL, 'v'},
+ {"help", 0, NULL, 'h'},
+ {"test", 0, NULL, 't'},
+ {"quiet", 0, NULL, 'q'},
+ {"trick", 0, NULL, 'k'},
+ {"output", 1, NULL, 'o'},
+ {"delay", 1, NULL, 'd'},
+ {"debug", 1, NULL, 'g'},
+ {NULL, 0, NULL, 0 }
+};
+#endif
+
+/*
+ * usage
+ */
+void
+usage ( void )
+{
+ fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION );
+ fprintf ( stderr, "usage: %s <[-oFILE] stream url> ...\n\n", PROGRAM_SHORT_NAME );
+
+#ifdef HAVE_GETOPT_LONG
+ fprintf ( stderr, "General Options:\n\t" \
+ "-a, --about\t\tshow information about %s\n\t" \
+ "-h, --help\t\tshow this help\n\t" \
+ "-v, --version\t\tshow version number\n\n" \
+ "Program Behaviour:\n\t" \
+ "-oFILE, --output=FILE\toutput to specified file (can be repeated)\n\t" \
+ "-gFILE, --debug=FILE\toutput debug info to specified file\n\t" \
+ "-q, --quiet\t\tquiet mode (can be repeated)\n\t" \
+ "-dDELAY, --delay=DELAY\tsave the stream during DELAY seconds and exit\n\t" \
+ "-k, --trick\t\tattempt to trick recalcitrant servers\n\t" \
+ "-t, --test\t\ttest mode (check stream availability)\n\n", PROGRAM_SHORT_NAME );
+#else
+ fprintf ( stderr, "General Options:\n\t" \
+ "-a\tshow information about %s\n\t" \
+ "-h\tshow this help\n\t" \
+ "-v\tshow version number\n\n" \
+ "Program Behaviour:\n\t" \
+ "-oFILE\toutput to specified file (can be repeated)\n\t" \
+ "-gFILE\toutput debug info to specified file\n\t" \
+ "-q\tquiet mode (can be repeated)\n\t" \
+ "-dDELAY\tsave the stream during DELAY seconds and exit\n\t" \
+ "-k\tattempt to trick recalcitrant servers\n\t" \
+ "-t\ttest mode (check stream availability)\n\n", PROGRAM_SHORT_NAME );
+#endif
+
+ return;
+}
+
+
+/*
+ * main
+ */
+int
+main ( int argc,
+ char *argv[] )
+{
+ MMS *mms;
+ FILE *f;
+ FILE *stddebug = NULL;
+ char *output_filename;
+ STREAM_LIST *stream_list;
+ STREAM_LIST *block;
+ char c;
+ int ret = MMS_RET_SUCCESS;
+ int quiet = 0;
+ int trick = MMS_TRICK_DISABLED;
+ int retry = 0;
+ int test = 0;
+ int delay = 0;
+ time_t end = 0;
+ float speed = 0.0f;
+ struct timeval speed_last_update;
+ struct timeval now;
+ float elapsed_time;
+ ssize_t len_written;
+ uint64_t total_len_written = 0;
+ uint64_t old_total_len_written = 0;
+
+ if ( ( stream_list = (STREAM_LIST *) malloc(sizeof(STREAM_LIST)) ) == NULL )
+ {
+ error ( "main", "early initialization failed" );
+ return 1;
+ }
+
+ stream_list->next = NULL;
+ stream_list->stream = NULL;
+ stream_list->output = NULL;
+
+ block = stream_list;
+
+#ifdef HAVE_GETOPT_LONG
+ while ( ( c = getopt_long(argc, argv, options, long_options, NULL) ) != -1 )
+#elif defined(SOLARIS) || defined(sun)
+ /* Implementation of getopt in Solaris is a bit strange, it returns -1 even if there are still args to parse... */
+ while ( optind < argc )
+#else
+ while ( ( c = getopt(argc, argv, options) ) != -1 )
+#endif
+ {
+#if defined(SOLARIS) || defined(sun)
+ c = getopt ( argc, argv, options );
+#endif
+ switch (c)
+ {
+ case 'h':
+ {
+ fprintf ( stderr, "\n" );
+ usage();
+ return 0;
+ }
+
+ case 'v':
+ {
+ fprintf ( stderr, "%s version %s\n", PROGRAM_SHORT_NAME, PROGRAM_VERSION);
+ return 0;
+ }
+
+ case 'a':
+ {
+ fprintf ( stderr, "\n" );
+ fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION);
+ fprintf ( stderr, "Written by %s.\n", PROGRAM_AUTHORS );
+ fprintf ( stderr, "With a lot of help from %s.\n\n", PROGRAM_HELPERS );
+ fprintf ( stderr, "This program is free software; you can redistribute it and/or\nmodify it under the terms " \
+ "of the GNU General Public License\nas published by the Free Software Foundation; either version 2" \
+ ",\nor (at your option) any later version.\n\n" );
+ return 0;
+ }
+
+ case 't':
+ {
+ test = 1;
+ break;
+ }
+
+ case 'q':
+ {
+ quiet += 1;
+ break;
+ }
+
+ case 'k':
+ {
+ trick = MMS_TRICK_ENABLED;
+ break;
+ }
+
+ case 'o':
+ {
+ if ( optarg != NULL )
+ {
+ if ( block->stream != NULL )
+ {
+ if ( ( block->next = malloc ( sizeof(STREAM_LIST) ) ) == NULL )
+ {
+ error ( "main", "early initialization failed" );
+ ret = MMS_RET_ERROR;
+ goto clean;
+ }
+
+ block = block->next;
+ block->next = NULL;
+ block->stream = NULL;
+ }
+
+ if ( block->output != NULL )
+ free ( block->output );
+
+ block->output = (char *) strdup ( optarg );
+ }
+
+ break;
+ }
+
+ case 'g':
+ {
+ if ( optarg != NULL )
+ {
+ if ( stddebug != NULL )
+ fclose ( stddebug );
+
+ if ( ( stddebug = fopen ( optarg, "w" ) ) == NULL )
+ {
+ if ( quiet < 2 )
+#ifdef HAVE_VSNPRINTF
+ warning ( NULL, "unable to write debug info in \'%s\'", optarg );
+#else
+ warning ( NULL, "unable to write debug info in specified file" );
+#endif
+ }
+ else
+ {
+ fprintf ( stddebug, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION);
+ fprintf ( stddebug, "--> debug log begins now\n" );
+ }
+ }
+
+ break;
+ }
+
+ case 'd':
+ {
+ if ( optarg != NULL )
+ {
+ delay = atoi( optarg );
+
+ if ( delay < 0 )
+ delay = 0;
+ }
+
+ break;
+ }
+
+
+#if defined(SOLARIS) || defined(sun)
+ case -1:
+ {
+ if ( optind >= argc )
+ break;
+#else
+ case 1:
+ {
+ if ( optarg != NULL )
+ {
+#endif
+ if ( block->stream != NULL )
+ {
+ if ( ( block->next = malloc ( sizeof(STREAM_LIST) ) ) == NULL )
+ {
+ error ( "main", "early initialization failed" );
+ ret = MMS_RET_ERROR;
+ goto clean;
+ }
+
+ block = block->next;
+ block->next = NULL;
+ block->output = NULL;
+ }
+#if defined(SOLARIS) || defined(sun)
+ /* optind is not incremented when meeting something else than an option, so we do that... */
+ block->stream = (char *) strdup ( argv[optind++] );
+#else
+ block->stream = (char *) strdup ( optarg );
+ }
+#endif
+ break;
+ }
+ }
+ }
+
+ if ( stream_list->stream == NULL )
+ {
+ usage ( );
+ ret = MMS_RET_ERROR;
+ goto clean;
+ }
+
+ if ( !quiet )
+ {
+ fprintf ( stderr, "\n" );
+ fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION );
+ }
+
+ for ( block=stream_list; block!=NULL; block=(STREAM_LIST*)block->next )
+ {
+ if ( block->stream == NULL )
+ {
+ if ( quiet < 2 )
+ {
+ if ( block->output == NULL )
+#ifdef HAVE_VSNPRINTF
+ error ( "main", "invalid invocation of %s", PROGRAM_SHORT_NAME );
+#else
+ error ( "main", "invalid invocation of mmsrip" );
+#endif
+ else
+#ifdef HAVE_VSNPRINTF
+ error ( "main", "output to \'%s\' is not attached to any stream", block->output );
+#else
+ error ( "main", "one of the -o output is not attached to any stream" );
+#endif
+ }
+
+ ret = MMS_RET_ERROR;
+ goto clean;
+ }
+
+ if ( block->output == NULL )
+ {
+ char *tmp = strchr ( block->stream, '/' );
+ char *interro_ptr = strchr ( block->stream, '?' );
+
+ if ( interro_ptr == NULL )
+ output_filename = strrchr ( block->stream, '/' );
+ else
+ {
+ do /* we look for the last '/' before the first '?' */
+ {
+ output_filename = tmp;
+ tmp = strchr ( tmp+1, '/' );
+ }
+ while ( ( tmp < interro_ptr ) && ( tmp != NULL ) );
+ }
+
+ if ( output_filename == NULL )
+ {
+ if ( quiet < 2 )
+#ifdef HAVE_VSNPRINTF
+ error ( "main", "malformed url: \'%s\'", block->stream );
+#else
+ error ( "main", "malformed url" );
+#endif
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ ++output_filename;
+
+ if ( strlen ( output_filename ) == 0 )
+ {
+ if ( quiet < 2 )
+#ifdef HAVE_VSNPRINTF
+ error ( "main", "malformed url: \'%s\'", block->stream );
+#else
+ error ( "main", "malformed url" );
+#endif
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ block->output = (char *) strdup ( output_filename );
+
+ /* we clean filenames that look like 'stream.asf?digest=7Q2bjXo&provider=lala' */
+ if ( ( interro_ptr = strchr(block->output,'?') ) != NULL )
+ *interro_ptr = '\0';
+ }
+ }
+
+ if ( ret != MMS_RET_SUCCESS )
+ goto clean;
+
+ if ( delay != 0 )
+ end = time(NULL) + delay;
+
+ for ( block=stream_list; block!=NULL; block=(STREAM_LIST*)(retry?block:block->next) )
+ {
+ output_filename = block->output;
+ retry = 0;
+
+ if ( !test )
+ {
+ if ( ( f = fopen ( output_filename, "w" ) ) == NULL )
+ {
+ if ( quiet < 2 )
+#ifdef HAVE_VSNPRINTF
+ error ( "main", "unable to write in \'%s\'", output_filename );
+#else
+ error ( "main", "unable to write in output file" );
+#endif
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+ }
+ else
+ f = NULL;
+
+ if ( ( mms = mms_create ( block->stream, f, stddebug, trick, test?1:(quiet>>1) ) ) == NULL )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to create mms struct" );
+
+ if ( !test )
+ {
+ fclose ( f );
+ remove ( output_filename );
+ }
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ if ( !quiet )
+ fprintf ( stderr, "Connecting ...\r" );
+
+ if ( mms_connect ( mms ) != MMS_RET_SUCCESS )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to connect" );
+
+ mms_destroy ( mms );
+
+ if ( !test )
+ {
+ fclose ( f );
+ remove ( output_filename );
+ }
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ if ( !quiet )
+ fprintf ( stderr, "Handshaking ...\r" );
+
+ if ( mms_handshake ( mms ) != MMS_RET_SUCCESS )
+ {
+ if ( ( quiet < 2 ) && ( !test ) )
+ error ( "main", "unable to handshake" );
+
+ mms_disconnect ( mms );
+ mms_destroy ( mms );
+
+ if ( !test )
+ {
+ fclose ( f );
+ remove ( output_filename );
+ }
+
+ if ( !quiet )
+ {
+ if ( !test )
+ fprintf ( stderr, "Stream \'%s\' is not good.\n\n", block->stream );
+ else
+ fprintf ( stderr, "Stream \'%s\' is not good.\n", block->stream );
+ }
+
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ if ( test )
+ {
+ if ( !quiet )
+ fprintf ( stderr, "Stream \'%s\' is available.\n", block->stream );
+
+ mms_disconnect ( mms );
+ mms_destroy ( mms );
+ continue;
+ }
+
+ if ( !quiet )
+ fprintf ( stderr, "Getting header ...\r" );
+
+ if ( ( len_written = mms_write_stream_header ( mms ) ) == MMS_RET_ERROR )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to write stream header" );
+
+ mms_disconnect ( mms );
+ mms_destroy ( mms );
+ fclose ( f );
+ remove ( output_filename );
+ ret = MMS_RET_ERROR;
+ continue;
+ }
+
+ total_len_written = len_written;
+
+ if ( !quiet )
+ fprintf ( stderr, "Rip is about to start ...\r" );
+
+ if ( mms_begin_rip ( mms ) != MMS_RET_SUCCESS )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to begin the rip" );
+
+ mms_disconnect ( mms );
+ mms_destroy ( mms );
+ fclose ( f );
+ remove ( output_filename );
+ ret = -1;
+ continue;
+ }
+
+ if ( mms->is_live == MMS_LIVE )
+ {
+ if ( !quiet )
+ {
+
+ fprintf ( stderr, " \r" );
+ fprintf ( stderr, "%s: %d bytes written (--.- kbps)\r", output_filename, (ssize_t)total_len_written );
+ gettimeofday ( &speed_last_update, NULL );
+ }
+
+ while ( 1 )
+ {
+ len_written = mms_write_stream_data ( mms );
+
+ if ( len_written == 0 )
+ break;
+ else if ( len_written == MMS_RET_ERROR )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to write stream data" );
+
+ ret = MMS_RET_ERROR;
+ break;
+ }
+ else if ( len_written == MMS_RET_NO_AUTH )
+ {
+ if ( trick == MMS_TRICK_DISABLED )
+ {
+ /* we retry with the trick enabled */
+ if ( quiet < 2 )
+ fprintf ( stderr, "\r*** retrying with the anti-recalcitrant servers trick enabled ***\n" );
+
+ trick = MMS_TRICK_ENABLED;
+ retry = 1;
+ }
+ else
+ {
+ /* it's definitely not working */
+ if ( quiet < 2 )
+ error ( "main", "unable to write stream data" );
+
+ ret = MMS_RET_NO_AUTH;
+ }
+
+ break;
+ }
+
+ total_len_written += len_written;
+
+ if ( !quiet )
+ {
+ gettimeofday ( &now, NULL );
+
+ if ( now.tv_sec > speed_last_update.tv_sec )
+ {
+ elapsed_time = (now.tv_sec - speed_last_update.tv_sec) + ((now.tv_usec - speed_last_update.tv_usec) / 1000000.0f);
+
+ if ( elapsed_time >= 1.0f )
+ {
+ speed = ( ( ((float) (total_len_written-old_total_len_written)) / elapsed_time) / 1024.0f );
+ old_total_len_written = total_len_written;
+ gettimeofday ( &speed_last_update, NULL );
+ }
+ }
+
+ if ( speed > 0.0f )
+ {
+ if ( (speed / 1024.0f) >= 1.0f )
+ fprintf ( stderr, "%s: %d bytes written (%.1f mbps) \r", output_filename, (ssize_t)total_len_written, speed/1024.0f );
+ else
+ fprintf ( stderr, "%s: %d bytes written (%.1f kbps) \r", output_filename, (ssize_t)total_len_written, speed );
+ }
+ else
+ fprintf ( stderr, "%s: %d bytes written (--.- kbps) \r", output_filename, (ssize_t)total_len_written );
+ }
+
+ fflush ( f );
+
+ if ( delay != 0 )
+ {
+ if ( end <= time(NULL) )
+ {
+ delay = -1;
+ break;
+ }
+ }
+ }
+
+ /* for a live, we should rewrite the header */
+
+ if ( ( ret == MMS_RET_SUCCESS ) && ( !quiet ) && ( !retry ) )
+ fprintf ( stderr, "%s: %d bytes written \r", output_filename, (ssize_t)total_len_written );
+ }
+ else
+ {
+ register const double coef = 100.0 / (double) mms->expected_file_size;
+
+ if ( !quiet )
+ {
+ fprintf ( stderr, " \r" );
+ fprintf ( stderr, "%s: %.2f%% (--.- kbps)\r", output_filename, (double) total_len_written * coef );
+ gettimeofday ( &speed_last_update, NULL );
+ }
+
+ while ( 1 )
+ {
+ len_written = mms_write_stream_data ( mms );
+
+ if ( len_written == 0 )
+ break;
+ else if ( len_written == MMS_RET_ERROR )
+ {
+ if ( quiet < 2 )
+ error ( "main", "unable to write stream data" );
+
+ ret = MMS_RET_ERROR;
+ break;
+ }
+ else if ( len_written == MMS_RET_NO_AUTH )
+ {
+ if ( trick == MMS_TRICK_DISABLED )
+ {
+ /* we retry with the trick enabled */
+ if ( quiet < 2 )
+ fprintf ( stderr, "\r*** retrying with the anti-recalcitrant servers trick enabled ***\n" );
+
+ trick = MMS_TRICK_ENABLED;
+ retry = 1;
+ }
+ else
+ {
+ /* it's definitely not working */
+ if ( quiet < 2 )
+ error ( "main", "unable to write stream data" );
+
+ ret = MMS_RET_NO_AUTH;
+ }
+
+ break;
+ }
+
+ total_len_written += len_written;
+
+ if ( !quiet )
+ {
+ gettimeofday ( &now, NULL );
+
+ if ( now.tv_sec > speed_last_update.tv_sec )
+ {
+ elapsed_time = (now.tv_sec - speed_last_update.tv_sec) + ((now.tv_usec - speed_last_update.tv_usec) / 1000000.0f);
+
+ if ( elapsed_time >= 1.0f )
+ {
+ speed = ( ( ((float) (total_len_written-old_total_len_written)) / elapsed_time) / 1024.0f );
+ old_total_len_written = total_len_written;
+ gettimeofday ( &speed_last_update, NULL );
+ }
+ }
+
+ if ( speed > 0.0f )
+ {
+ if ( (speed / 1024.0f) >= 1.0f )
+ fprintf ( stderr, "%s: %.2f%% (%.1f mbps) \r", output_filename, (double) total_len_written * coef, speed/1024.0f );
+ else
+ fprintf ( stderr, "%s: %.2f%% (%.1f kbps) \r", output_filename, (double) total_len_written * coef, speed );
+ }
+ else
+ fprintf ( stderr, "%s: %.2f%% (--.- kbps) \r", output_filename, (double) total_len_written * coef );
+ }
+
+ fflush ( f );
+
+ if ( delay != 0 )
+ {
+ if ( end <= time(NULL) )
+ {
+ delay = -1;
+ break;
+ }
+ }
+ }
+
+ if ( ( ret == MMS_RET_SUCCESS ) && ( !quiet ) && ( delay != -1 ) && ( !retry ) )
+ fprintf ( stderr, "%s: 100.00%% \r", output_filename );
+ }
+
+ mms_disconnect ( mms );
+ mms_destroy ( mms );
+
+ fclose ( f );
+
+ if ( delay == -1 )
+ break;
+ }
+
+ clean:
+ if ( !quiet )
+ fprintf ( stderr, "\n" );
+
+ if ( stddebug != NULL )
+ {
+ fprintf ( stddebug, "\n\n--> debug log ends now\n" );
+ fclose ( stddebug );
+ }
+
+ for ( block=stream_list; block!=NULL; )
+ {
+ STREAM_LIST *old;
+ old = block;
+ block = (STREAM_LIST *) block->next;
+
+ if ( old->stream != NULL )
+ free ( old->stream );
+
+ if ( old->output != NULL )
+ free ( old->output );
+
+ free ( old );
+ }
+
+ return (ret<0) ? (ret*-1) : ret;
+}
diff --git a/src/mmsrip/mms.c b/src/mmsrip/mms.c
new file mode 100644
index 0000000..022f7ae
--- /dev/null
+++ b/src/mmsrip/mms.c
@@ -0,0 +1,1239 @@
+/*
+ * $RCSfile: mms.c,v $
+ * $Date: 2006/01/23 20:30:43 $ - $Revision: 1.33 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * It is highly based on the work of SDP Multimedia and Major MMS.
+ * They deserve all the credits for it.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#define _GNU_SOURCE
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <unistd.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "common.h"
+#include "mms.h"
+#include "error.h"
+
+
+/*
+ * Put 32 bits inside a command buffer.
+ */
+static void
+mms_put_32 ( MMS_PACKET *pak,
+ uint32_t value )
+{
+ pak->buf[pak->num_bytes ] = value % 256;
+ value = value >> 8;
+ pak->buf[pak->num_bytes+1] = value % 256 ;
+ value = value >> 8;
+ pak->buf[pak->num_bytes+2] = value % 256 ;
+ value = value >> 8;
+ pak->buf[pak->num_bytes+3] = value % 256 ;
+
+ pak->num_bytes += 4;
+ return;
+}
+
+
+/*
+ * Returns the 32 bits located at specified offset.
+ */
+static uint32_t
+mms_get_32 ( const unsigned char *buf,
+ const int offset )
+{
+ return ( (((uint32_t)buf[offset+0]) << 0) |
+ (((uint32_t)buf[offset+1]) << 8) |
+ (((uint32_t)buf[offset+2]) << 16) |
+ (((uint32_t)buf[offset+3]) << 24) );
+}
+
+
+/*
+ * Returns the 64 bits located at specified offset.
+ */
+static uint64_t
+mms_get_64 ( const unsigned char *buf,
+ const int offset )
+{
+ return ( (((uint64_t)buf[offset+0]) << 0) |
+ (((uint64_t)buf[offset+1]) << 8) |
+ (((uint64_t)buf[offset+2]) << 16) |
+ (((uint64_t)buf[offset+3]) << 24) |
+ (((uint64_t)buf[offset+4]) << 32) |
+ (((uint64_t)buf[offset+5]) << 40) |
+ (((uint64_t)buf[offset+6]) << 48) |
+ (((uint64_t)buf[offset+7]) << 56) );
+}
+
+
+/*
+ * Converts a string into a UTF-16 string.
+ */
+static void
+mms_string_utf16 ( uint8_t *dest,
+ unsigned char *src,
+ const ssize_t len )
+{
+ ssize_t i;
+ memset ( dest, 0, len );
+
+ for ( i=0; i<len; ++i )
+ {
+ dest[i*2] = src[i];
+ dest[i*2+1] = 0;
+ }
+
+ dest[i*2] = 0;
+ dest[i*2+1] = 0;
+ return;
+}
+
+
+/*
+ * Checks a URL for a correct MMS protocol.
+ * Returns the host name index if the protocol is valid, else it returns MMS_RET_ERROR.
+ */
+static int
+mms_check_protocol ( const char *url )
+{
+ static const char *proto[] = { "mms://", "mmsu://", "mmst://", "mmsh://", NULL };
+ static const int proto_len[] = { 6, 7, 7, 7, 0 };
+ register const char *p = proto[0];
+ int i = 0;
+
+ while ( p != NULL )
+ {
+ if ( strncmp ( url, p, proto_len[i] ) == 0 )
+ return proto_len[i];
+
+ p = proto[++i];
+ }
+
+ return MMS_RET_ERROR;
+}
+
+
+/*
+ * Prints out a command packet (debug purpose).
+ */
+static void
+mms_print_packet ( FILE *stddebug,
+ const MMS_PACKET *pak,
+ const ssize_t length,
+ const int orig )
+{
+ ssize_t i;
+ unsigned char c;
+
+ fprintf ( stddebug, "\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n" );
+
+ if ( orig == MMS_SERVER )
+ fprintf ( stddebug, " command from server (%d bytes)\n", length );
+ else
+ fprintf ( stddebug, " command from client (%d bytes)\n", length );
+
+ fprintf ( stddebug, " start sequence: 0x%08x\n", mms_get_32(pak->buf, 0) );
+ fprintf ( stddebug, " command id: 0x%08x\n", mms_get_32(pak->buf, 4) );
+ fprintf ( stddebug, " length: 0x%8x \n", mms_get_32(pak->buf, 8) );
+ fprintf ( stddebug, " len8: 0x%8x \n", mms_get_32(pak->buf, 16) );
+ fprintf ( stddebug, " sequence #: 0x%08x\n", mms_get_32(pak->buf, 20) );
+ fprintf ( stddebug, " len8 (II): 0x%8x \n", mms_get_32(pak->buf, 32) );
+ fprintf ( stddebug, " dir | comm: 0x%08x\n", mms_get_32(pak->buf, 36) );
+ fprintf ( stddebug, " switches: 0x%08x\n", mms_get_32(pak->buf, 40) );
+
+ fprintf ( stddebug, "\nascii contents:\n" );
+
+ for ( i=48; i<length; i+=2 )
+ {
+ c = pak->buf[i];
+
+ if ( ( c>=32 ) && ( c<=128 ) )
+ fprintf ( stddebug, "%c", c );
+ else
+ fprintf ( stddebug, "." );
+ }
+
+ fprintf ( stddebug, "\n\npackage hexdump:\n " );
+
+ i = 0;
+
+ while ( 1 )
+ {
+ c = pak->buf[i];
+ fprintf ( stddebug, "%02x", c );
+
+ ++i;
+
+ if ( i < length )
+ {
+ if ( (i % 16) == 0 )
+ fprintf ( stddebug, "\n" );
+
+ if ( (i % 2) == 0 )
+ fprintf ( stddebug, " " );
+ }
+ else
+ break;
+ }
+
+ fprintf ( stddebug, "\n\n" );
+ fflush ( stddebug );
+ return;
+}
+
+
+/*
+ * Sends a command packet to the server.
+ * Returns MMS_RET_SUCCESS if success, MMS_RET_ERROR either.
+ */
+static int
+mms_send_packet ( MMS *mms,
+ const int command,
+ const uint32_t switches,
+ const uint32_t extra,
+ const ssize_t length,
+ const uint8_t *data )
+{
+ MMS_PACKET pak;
+ ssize_t written_len;
+
+ const ssize_t len8 = ( length + (length%8) ) / 8;
+
+ pak.num_bytes = 0;
+
+ mms_put_32 ( &pak, 0x00000001 ); /* start sequence */
+ mms_put_32 ( &pak, 0xB00BFACE ); /* #-)) */
+ mms_put_32 ( &pak, length + 32 );
+ mms_put_32 ( &pak, 0x20534d4d ); /* protocol type "MMS " */
+ mms_put_32 ( &pak, len8 + 4 );
+ mms_put_32 ( &pak, mms->seq_num++ );
+ mms_put_32 ( &pak, 0x0 ); /* unknown */
+ mms_put_32 ( &pak, 0x0 );
+ mms_put_32 ( &pak, 2+len8 );
+ mms_put_32 ( &pak, 0x00030000 | command ); /* dir | command */
+ mms_put_32 ( &pak, switches );
+ mms_put_32 ( &pak, extra );
+
+ memcpy ( &pak.buf[48], data, length );
+
+ written_len = write ( mms->socket, pak.buf, length+48 );
+
+ if ( written_len == -1 )
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_send_packet", "write() said: %s", strerror(errno) );
+#else
+ error ( "mms_send_packet", "write() failed" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+ else if ( written_len != (length+48) )
+ {
+ if ( !mms->quiet )
+ error ( "mms_send_packet", "did not write enough bytes" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &pak, length+48, MMS_CLIENT );
+
+ return MMS_RET_SUCCESS;
+}
+
+
+/*
+ * Reads some bytes from the socket.
+ * This is blocking until we don't have the required amount of bytes.
+ * Returns MMS_RET_SUCCESS if success, MMS_RET_ERROR either.
+ */
+static int
+mms_recv_packet ( const int s,
+ MMS_PACKET *pak,
+ size_t count,
+ const int quiet )
+{
+ ssize_t read_len, total;
+ total = 0;
+
+ if ( MMS_BUF_SIZE < count )
+ {
+ if ( !quiet )
+ warning ( "mms_recv_packet", "caller is too greedy" );
+
+ count = MMS_BUF_SIZE;
+ }
+
+ while ( total < count )
+ {
+ read_len = read ( s, &pak->buf[total], count-total );
+
+ if ( read_len == -1 )
+ {
+ if ( !quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_recv_packet", "read() said: %s", strerror(errno) );
+#else
+ error ( "mms_recv_packet", "read() failed" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+
+ total += read_len;
+ }
+
+ return MMS_RET_SUCCESS;
+}
+
+
+/*
+ * Gets server's command.
+ * The first 8 bytes can be skipped setting offset to 8.
+ * It writes the packet length in the provided storage.
+ * Returns command ID, MMS_CMD_INVALID if error.
+ */
+static int
+mms_recv_cmd_packet ( const int s,
+ MMS_PACKET *pak,
+ ssize_t *packet_len,
+ const int offset,
+ const int quiet )
+{
+ MMS_PACKET tmp;
+
+ if ( ( offset > 8 ) || ( offset < 0 ) )
+ {
+ if ( !quiet )
+ error ( "mms_recv_cmd_packet", "provided offset is invalid" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms_recv_packet ( s, &tmp, 12-offset, quiet ) != 0 )
+ {
+ if ( !quiet )
+ error ( "mms_recv_cmd_packet", "unable to get packet header" );
+
+ return MMS_RET_ERROR;
+ }
+
+ memcpy ( &pak->buf[offset], tmp.buf, 12-offset );
+
+ if ( offset == 0 )
+ {
+ if ( mms_get_32(pak->buf, 4) != 0xb00bface ) /* probably a coincidence... */
+ {
+ if ( !quiet )
+ error ( "mms_recv_cmd_packet", "answer should have been a cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+ }
+
+ memcpy ( packet_len, &pak->buf[8], 4 );
+ *packet_len = mms_get_32 ( (unsigned char *) packet_len, 0 ) + 4;
+
+ if ( ( *packet_len + 12 ) > MMS_BUF_SIZE )
+ {
+ if ( !quiet )
+ error ( "mms_recv_cmd_packet", "incoming packet is too big for me" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms_recv_packet ( s, &tmp, *packet_len, quiet ) != 0 )
+ {
+ if ( !quiet )
+ error ( "mms_recv_cmd_packet", "unable to get packet body" );
+
+ return MMS_RET_ERROR;
+ }
+
+ memcpy ( &pak->buf[12], tmp.buf, *packet_len );
+
+ return ( mms_get_32(pak->buf, 36) & 0xFFFF );
+}
+
+
+/*
+ * Gets the header of the ASF/WMV stream/file.
+ * Returns the amount of bytes received, MMS_RET_ERROR if we encounter an error.
+ */
+static ssize_t
+mms_recv_header_packet ( MMS *mms,
+ MMS_PACKET *pak )
+{
+ MMS_PACKET pre_header;
+ MMS_PACKET tmp;
+ ssize_t header_len;
+ ssize_t packet_len;
+
+ header_len = 0;
+
+ while ( 1 )
+ {
+ if ( mms_recv_packet ( mms->socket, &pre_header, 8, mms->quiet ) != 0 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_header_packet", "unable to get pre-header" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( pre_header.buf[4] == 0x02 )
+ {
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n" );
+ fprintf ( mms->stddebug, " getting header packet from server\n\n" );
+ }
+
+ if ( mms->stddebug != NULL )
+ {
+ static int i; /* static, so we don't recreate it for each call */
+
+ for ( i=0; i<8; ++i )
+ fprintf ( mms->stddebug, "pre_header.buf[%d] = 0x%02x (%d)\n", i, pre_header.buf[i], pre_header.buf[i] );
+ }
+
+ packet_len = ( pre_header.buf[7] << 8 | pre_header.buf[6] ) - 8;
+
+ if ( mms->stddebug != NULL )
+ fprintf ( mms->stddebug, "\nASF Header Packet (%d bytes)\n", packet_len );
+
+ if ( mms_recv_packet ( mms->socket, &tmp, packet_len, mms->quiet ) != 0 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_header_packet", "unable to get header" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( ( header_len + packet_len ) > MMS_BUF_SIZE )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_header_packet", "total header length is too big for me" );
+
+ return MMS_RET_ERROR;
+ }
+
+ memcpy ( &pak->buf[header_len], tmp.buf, packet_len );
+
+ header_len += packet_len;
+
+ if ( mms->stddebug != NULL )
+ fprintf ( mms->stddebug, "\n" );
+
+ /* test if end of header is reached */
+ if ( ( pak->buf[header_len-1] == 1) &&
+ ( pak->buf[header_len-2] == 1 ) )
+ return header_len;
+ }
+ else /* we might receive an ack or an error */
+ {
+ int command = mms_recv_cmd_packet ( mms->socket, &tmp, &packet_len, 8, mms->quiet );
+
+ if ( command == -1 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_header_packet", "unable to get cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+
+ memcpy ( tmp.buf, pre_header.buf, 8 );
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &tmp, packet_len, MMS_SERVER );
+
+ if ( command == MMS_CMD_PING )
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, tmp.buf );
+ else if ( command != MMS_CMD_HEADER_DATA )
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_recv_header_packet", "unknown command 0x%02x\n", command );
+#else
+ error ( "mms_recv_header_packet", "unknown command\n" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+ }
+ }
+}
+
+
+/*
+ * Interprates the header (media packet size, etc.).
+ * Returns the size of a media packet (should be != 0 ).
+ */
+static ssize_t
+mms_interp_header ( MMS *mms,
+ const uint8_t *header,
+ const ssize_t header_len )
+{
+ int i;
+ ssize_t packet_length = 0;
+
+ mms->expected_file_size = header_len;
+
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n" );
+ fprintf ( mms->stddebug, " header interpretation\n\n" );
+ }
+
+ /* parse header */
+ mms->num_stream_ids = 0;
+ i = 30;
+
+ while ( i < header_len )
+ {
+ uint64_t guid_1, guid_2, length;
+
+ if ( mms->stddebug != NULL )
+ {
+ /* this is MS GUID format */
+ fprintf ( mms->stddebug, "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
+ header[i+3], header[i+2], header[i+1], header[i+0],
+ header[i+5], header[i+4], header[i+7], header[i+6],
+ header[i+8], header[i+9], header[i+10], header[i+11],
+ header[i+12], header[i+13], header[i+14], header[i+15] );
+ }
+
+ guid_2 = mms_get_64 ( header, i );
+ i += 8;
+
+ guid_1 = mms_get_64 ( header, i );
+ i += 8;
+
+ length = mms_get_64 ( header, i );
+ i += 8;
+
+ if ( ( guid_1 == 0x6553200cc000e48eULL ) && ( guid_2 == 0x11cfa9478cabdca1ULL ) )
+ {
+ packet_length = mms_get_32 ( header, i+92-24 );
+
+ if ( packet_length != mms_get_32(header, i+72) )
+ warning ( NULL, "size of media packets is not constant, it should not happen" );
+
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "File Properties Object (%lld bytes)\n", length );
+ fprintf ( mms->stddebug, " -> broadcast bit: %d\n", mms_get_32(header,i+64)&1 );
+ fprintf ( mms->stddebug, " -> min packet length: %d\n", packet_length );
+ fprintf ( mms->stddebug, " -> max packet length: %d\n", mms_get_32(header, i+72) );
+ fprintf ( mms->stddebug, " -> number of media packets: %d\n\n", mms_get_32(header, i+32) );
+ }
+
+ if ( mms_get_32(header,i+64)&1 )
+ {
+ if ( ( !mms->quiet ) && ( !mms->is_live ) )
+ warning ( NULL, "stream seems to be live, an error may occur" );
+
+ mms->is_live = MMS_LIVE;
+ }
+ }
+ else if ( ( guid_1 == 0x6553200cc000e68eULL ) && ( guid_2 == 0x11cfa9b7b7dc0791ULL ) )
+ {
+ int stream_id = header[i+48] | header[i+49] << 8;
+
+ if ( mms->num_stream_ids < 20 )
+ {
+ mms->stream_ids[mms->num_stream_ids] = stream_id;
+ ++mms->num_stream_ids;
+ }
+ else
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ warning ( "mms_interp_header", "too many streams, stream \'%d\' skipped", stream_id );
+#else
+ warning ( "mms_interp_header", "too many streams, skipping.." );
+#endif
+ }
+
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "Stream Object (%lld bytes)\n", length );
+ fprintf ( mms->stddebug, " -> stream id: %d\n\n", stream_id );
+ }
+ }
+ else if ( ( guid_1 == 0x6cce6200aa00d9a6ULL ) && ( guid_2 == 0x11cf668e75b22636ULL ) )
+ {
+ mms->expected_file_size += ( length - 50 ); /* valid values are at least 50 bytes (why?) */
+
+ if ( mms->stddebug != NULL )
+ fprintf ( mms->stddebug, "Data Object (%lld bytes)\n\n", length );
+ }
+ else if ( mms->stddebug != NULL )
+ {
+ if ( ( guid_1 == 0x6cce6200aa00d9a6ULL ) && ( guid_2 == 0x11cf668e75b22630ULL ) )
+ fprintf ( mms->stddebug, "Header Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0xcb4903c9a000f489ULL ) && ( guid_2 == 0x11cfe5b133000890ULL) )
+ fprintf ( mms->stddebug, "Simple Index Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0xbe4903c9a0003490ULL ) && ( guid_2 == 0x11d135dad6e229d3ULL) )
+ fprintf ( mms->stddebug, "Index Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0x6553200cc000e38eULL ) && ( guid_2 == 0x11cfa92e5fbf03b5ULL) )
+ fprintf ( mms->stddebug, "Header Extension Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0x6cce6200aa00d9a6ULL ) && ( guid_2 == 0x11cf668e75b22633ULL) )
+ fprintf ( mms->stddebug, "Content Description Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0x50a85ec9a000f097ULL ) && ( guid_2 == 0x11d2e307d2d0a440ULL) )
+ fprintf ( mms->stddebug, "Extended Content Description Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0xf64803c9a000a4a3ULL ) && ( guid_2 == 0x11d0311d86d15240ULL) )
+ fprintf ( mms->stddebug, "Codec List Object (%lld bytes)\n\n", length );
+ else if ( ( guid_1 == 0xb2a2c9976000828dULL ) && ( guid_2 == 0x11d1468d7bf875ceULL) )
+ fprintf ( mms->stddebug, "Stream Bitrate Properties Object (%lld bytes)\n\n", length );
+ else
+ fprintf ( mms->stddebug, "Unknown Object (%lld bytes)\n\n", length );
+ }
+
+ i += length-24;
+ }
+
+ return packet_length;
+}
+
+
+/*
+ * Gets a media packet.
+ * Returns the amount of bytes in the media packet (0 if EOF), MMS_RET_ACKED if ACKed,
+ * MMS_RET_NO_AUTH if authorization failed, MMS_RET_ERROR either.
+ */
+static ssize_t
+mms_recv_media_packet ( MMS *mms,
+ MMS_PACKET *pak )
+{
+ MMS_PACKET pre_header;
+ ssize_t packet_len;
+
+ if ( mms_recv_packet ( mms->socket, &pre_header, 8, mms->quiet ) != 0 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_media_packet", "unable to get pre-header" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( pre_header.buf[4] == 0x04 )
+ {
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n" );
+ fprintf ( mms->stddebug, " getting media packet from server\n\n" );
+ }
+
+ if ( mms->stddebug != NULL )
+ {
+ static int i; /* static, so we don't recreate it for each call */
+
+ for ( i=0; i<8; i++ )
+ fprintf ( mms->stddebug, "pre_header[%d] = 0x%02x (%d)\n", i, pre_header.buf[i], pre_header.buf[i] );
+ }
+
+ packet_len = (pre_header.buf[7] << 8 | pre_header.buf[6]) - 8;
+
+ if ( mms->stddebug != NULL )
+ fprintf ( mms->stddebug, "\nASF Media Packet (%d bytes)\n", packet_len );
+
+ memset ( pak->buf, 0, mms->media_packet_len );
+
+ if ( mms_recv_packet (mms->socket, pak, packet_len, mms->quiet) != 0 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_media_packet", "unable to get media packet" );
+
+ return MMS_RET_ERROR;
+ }
+ }
+ else
+ {
+ int command = mms_recv_cmd_packet ( mms->socket, pak, &packet_len, 8, mms->quiet );
+
+ if ( command == MMS_RET_ERROR )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_media_packet", "unable to get cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+
+ memcpy ( pak->buf, pre_header.buf, 8 );
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, pak, packet_len, MMS_SERVER );
+
+ if ( command == MMS_CMD_PING )
+ {
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, pak->buf );
+ return MMS_RET_ACKED;
+ }
+ else if ( command == MMS_CMD_END_OF_STREAM )
+ return 0;
+ else if ( command == MMS_CMD_STREAM_SELECT_ACK ) /* it happens sometimes */
+ return MMS_RET_ACKED;
+ else if ( command == MMS_CMD_READY_TO_STREAM ) /* it always happen before the first media packet */
+ {
+ /* this happens on some server for some unknown reason */
+ if ( mms_get_32(pak->buf, 40) == 0x80070005 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_recv_media_packet", "streaming denied (read manpage & retry later)" );
+
+ return MMS_RET_NO_AUTH;
+ }
+
+ return MMS_RET_ACKED;
+ }
+ else
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_recv_media_packet", "unknown command 0x%02x\n", command );
+#else
+ error ( "mms_recv_media_packet", "unknown command\n" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+ }
+
+ if ( mms->stddebug != NULL )
+ fprintf ( mms->stddebug, "\n" );
+
+ return packet_len;
+}
+
+
+/*
+ * Creates An MMS Struct
+ * Returns an initialized MMS Struct if successful, NULL if an error occurs.
+ * url must start with mms:// or mmst:// (mmsu:// and mmsh:// are OK too, but we won't use UDP or do MMS over HTTP)
+ * trick must be MMS_TRICK_DISABLED or MMS_TRICK_ENABLED
+ */
+MMS *
+mms_create ( const char *url,
+ FILE *out,
+ FILE *stddebug,
+ const int trick,
+ const int quiet )
+{
+ MMS *mms;
+ register const char *sep;
+ register const int host_idx = mms_check_protocol ( url );
+ register const int url_len = strlen ( url );
+
+ if ( stddebug != NULL )
+ {
+ fprintf ( stddebug, "\n\n********************************************************************************\n\n" );
+ fprintf ( stddebug, "Url -> \'%s\'\n", url );
+ }
+
+ if ( host_idx == MMS_RET_ERROR )
+ {
+ if ( !quiet )
+ error ( "mms_create", "bad protocol (mms:// was expected)" );
+
+ return NULL;
+ }
+
+ sep = strchr ( &url[host_idx], '/' );
+
+ if ( sep == NULL )
+ {
+ if ( !quiet )
+ error ( "mms_create", "url seems to be malformed" );
+
+ return NULL;
+ }
+
+ if ( ( mms = ( MMS * ) malloc ( sizeof(MMS) ) ) == NULL )
+ {
+ if ( !quiet )
+ error ( "mms_create", "unable to allocate memory" );
+
+ return NULL;
+ }
+
+ mms->host = malloc ( ( sep - &url[host_idx] ) + 1 );
+ strncpy ( mms->host, &url[host_idx], (sep - &url[host_idx]) );
+ mms->host[sep-&url[host_idx]] = '\0'; /* strndup */
+
+ mms->path = strdup ( sep+1 );
+ mms->out = out;
+ mms->seq_num = 0;
+ mms->expected_file_size = 0;
+ mms->is_live = MMS_NO_LIVE;
+
+ /* we try to guess stream type with the filename extension */
+ if ( ( sep = strchr(sep,'?') ) != NULL )
+ {
+ if ( ( *(sep-4) == '.' ) && /* this will never segfault because */
+ ( *(sep-3) == 'w' ) && /* the url is at least 6 chars long (mms://) */
+ ( *(sep-2) == 'm' ) &&
+ ( *(sep-1) == 'v' ) )
+ mms->stream_type = MMS_WMV;
+ else
+ mms->stream_type = MMS_ASF;
+ }
+ else
+ {
+ if ( ( url[url_len-4] == '.' ) &&
+ ( url[url_len-3] == 'w' ) &&
+ ( url[url_len-2] == 'm' ) &&
+ ( url[url_len-1] == 'v' ) )
+ mms->stream_type = MMS_WMV;
+ else
+ mms->stream_type = MMS_ASF;
+ }
+
+ mms->stddebug = stddebug;
+ mms->quiet = quiet;
+ mms->trick = ( ((trick==MMS_TRICK_DISABLED)||(trick==MMS_TRICK_ENABLED)) ? trick : MMS_TRICK_DISABLED );
+
+ if ( mms->stddebug != NULL )
+ {
+ fprintf ( mms->stddebug, "Host -> \'%s\'\nPath -> \'%s\'\n", mms->host, mms->path );
+ fprintf ( mms->stddebug, "Stream type -> %s\n", (mms->stream_type==MMS_WMV)?"MMS_WMV":"MMS_ASF" );
+ }
+
+ return mms;
+}
+
+
+/*
+ * Establishes a connection with the MMS server.
+ * Returns MMS_RET_SUCCESS if success, MMS_RET_ERROR either.
+ */
+int
+mms_connect ( MMS* mms )
+{
+ struct sockaddr_in sa;
+ struct hostent *hp;
+
+ if ( mms == NULL )
+ return -1;
+
+ if ( ( hp = gethostbyname(mms->host) ) == NULL )
+ {
+ if ( !mms->quiet )
+ error ( "mms_connect", "unable to resolve host name" );
+
+ return MMS_RET_ERROR;
+ }
+
+ bcopy ( (char *) hp->h_addr, (char *) &sa.sin_addr, hp->h_length );
+ sa.sin_family = hp->h_addrtype;
+ sa.sin_port = htons ( 1755 );
+
+ if ( ( mms->socket = socket(hp->h_addrtype, SOCK_STREAM, 0) ) == -1 )
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_connect", "socket() said: %s", strerror(errno) );
+#else
+ error ( "mms_connect", "socket() failed" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( connect ( mms->socket, (struct sockaddr *) &sa, sizeof sa ) != 0 )
+ {
+ if ( !mms->quiet )
+#ifdef HAVE_VSNPRINTF
+ error ( "mms_connect", "connect() said: %s", strerror(errno) );
+#else
+ error ( "mms_connect", "connect failed" );
+#endif
+
+ return MMS_RET_ERROR;
+ }
+
+ return MMS_RET_SUCCESS;
+}
+
+
+/*
+ * Handshake with the MMS server.
+ * ( we don't care about server's answers )
+ */
+int
+mms_handshake ( MMS *mms )
+{
+ MMS_PACKET pak;
+ int cmd = MMS_CMD_PING;
+ ssize_t packet_len;
+ char str [ 1024 ];
+ uint8_t data [ 2092 ];
+
+ if ( mms == NULL )
+ return MMS_RET_ERROR;
+
+ /* who we are */
+ memset ( data, 0, sizeof (data) );
+
+ if ( mms->trick == MMS_TRICK_DISABLED )
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf ( str, sizeof(str), "\034\003NSPlayer/7.0.0.1956; {3300AD50-2C39-46c0-AE0A-60181587CBA}; Host: %s", mms->host );
+#else
+ if ( (strlen("\034\003NSPlayer/7.0.0.1956; {3300AD50-2C39-46c0-AE0A-60181587CBA}; Host: ")+strlen(mms->host)) < sizeof(str) )
+ sprintf ( str, "\034\003NSPlayer/7.0.0.1956; {3300AD50-2C39-46c0-AE0A-60181587CBA}; Host: %s", mms->host );
+ else
+ {
+ error ( "mms_handshake", "host name is too long" );
+ return MMS_RET_ERROR;
+ }
+#endif
+ }
+ else
+ strcpy ( str, "\034\003NSPlayer/4.1.0.3928; {3300AD50-2C39-46c0-AE0A-60181587CBA}" );
+
+ mms_string_utf16 ( data, (unsigned char *) str, strlen(str)+2 );
+ mms_send_packet ( mms, MMS_CMD_HELLO, 0, 0x0004000b, strlen(str)*2 + 6, data );
+
+ cmd = MMS_CMD_PING;
+ while ( cmd == MMS_CMD_PING )
+ {
+ cmd = mms_recv_cmd_packet ( mms->socket, &pak, &packet_len, 0, mms->quiet );
+
+ if ( cmd == MMS_CMD_PING )
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, pak.buf );
+ }
+
+ if ( cmd == MMS_CMD_INVALID )
+ {
+ if ( !mms->quiet )
+ error ( "mms_handshake", "unable to get cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &pak, packet_len, MMS_SERVER );
+
+ /* transport protocol selection */
+ memset ( &data, 0, sizeof(data) );
+ mms_string_utf16 ( &data[8], (unsigned char *) "\002\000\\\\192.168.0.129\\TCP\\1037\0000", 28 );
+ mms_send_packet ( mms, MMS_CMD_PROTOCOL_SELECT, 0, 0, 28*2+8, data );
+
+ cmd = MMS_CMD_PING;
+ while ( cmd == MMS_CMD_PING )
+ {
+ cmd = mms_recv_cmd_packet ( mms->socket, &pak, &packet_len, 0, mms->quiet );
+
+ if ( cmd == MMS_CMD_PING )
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, pak.buf );
+ }
+
+ if ( cmd == MMS_CMD_INVALID )
+ {
+ if ( !mms->quiet )
+ error ( "mms_handshake", "unable to get cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &pak, packet_len, MMS_SERVER );
+
+ /* requested file */
+ mms_string_utf16 ( &data[8], (unsigned char *) mms->path, strlen(mms->path) );
+ memset ( data, 0, 8 );
+ mms_send_packet ( mms, MMS_CMD_FILE_REQUEST, 0, 0, strlen(mms->path)*2+12, data );
+
+ cmd = MMS_CMD_PING;
+ while ( cmd == MMS_CMD_PING )
+ {
+ cmd = mms_recv_cmd_packet ( mms->socket, &pak, &packet_len, 0, mms->quiet );
+
+ if ( cmd == MMS_CMD_PING )
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, pak.buf );
+ }
+
+ if ( cmd == MMS_CMD_INVALID )
+ {
+ if ( !mms->quiet )
+ error ( "mms_handshake", "unable to get cmd packet" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( cmd == MMS_CMD_STREAM_INFOS )
+ {
+ if ( mms_get_32 ( pak.buf, 48 ) == -1 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_handshake", "stream infos are not available" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( ( mms_get_32(pak.buf,72) == 0 ) || ( mms_get_32(pak.buf,72) == 0xFFFFFFFF ) )
+ {
+ mms->is_live = MMS_LIVE;
+
+ if ( !mms->quiet )
+ warning ( NULL, "stream seems to be live, an error may occur" );
+ }
+ }
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &pak, packet_len, MMS_SERVER );
+
+ return MMS_RET_SUCCESS;
+}
+
+
+/*
+ * Gets ASF/WMV stream/file header
+ */
+ssize_t
+mms_write_stream_header ( MMS *mms )
+{
+ MMS_PACKET pak;
+ ssize_t len;
+
+ if ( mms == NULL )
+ return MMS_RET_ERROR;
+
+ /* give me the stream header please... */
+ memset ( pak.buf, 0, 40 );
+ pak.buf[32] = 2;
+ mms_send_packet ( mms, MMS_CMD_HEADER_REQUEST, 1, 0, 40, pak.buf );
+
+ len = mms_recv_header_packet ( mms, &pak );
+
+ if ( len == -1 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_get_stream_header", "unable to get stream header\n" );
+
+ return MMS_RET_ERROR;
+ }
+ else if ( len == 0 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_get_stream_header", "no stream header available, file may not exist" );
+
+ return MMS_RET_ERROR;
+ }
+
+ fwrite ( pak.buf, len, 1, mms->out );
+
+ mms->media_packet_len = mms_interp_header ( mms, pak.buf, len );
+
+ if ( mms->media_packet_len > MMS_BUF_SIZE )
+ {
+ if ( !mms->quiet )
+ error ( "mms_get_stream_header", "media packet length is too big for me" );
+
+ return MMS_RET_ERROR;
+ }
+
+ return len;
+}
+
+
+/*
+ * Tells the server we're ready to rip the stream from the beginning (don't know what happens if it's a live)
+ * Returns 0 if success, -1 either.
+ */
+int
+mms_begin_rip ( MMS *mms )
+{
+ int i;
+ uint8_t data [ 1024 ];
+ MMS_PACKET pak;
+ ssize_t packet_len;
+ int cmd;
+
+ if ( mms == NULL )
+ return MMS_RET_ERROR;
+
+ /* media stream selection */
+ memset ( data, 0, 40 );
+
+ for ( i=1; i<mms->num_stream_ids; ++i )
+ {
+ data [ (i-1) * 6 + 2 ] = 0xFF;
+ data [ (i-1) * 6 + 3 ] = 0xFF;
+ data [ (i-1) * 6 + 4 ] = mms->stream_ids[i];
+ data [ (i-1) * 6 + 5 ] = 0x00;
+ }
+
+ /* we add the final bytes of the stream selector, necessary for ASF streams only */
+ if ( mms->stream_type == MMS_ASF )
+ {
+ data [ (mms->num_stream_ids-1) * 6 + 0 ] = 0x00;
+ data [ (mms->num_stream_ids-1) * 6 + 1 ] = 0x00;
+ data [ (mms->num_stream_ids-1) * 6 + 2 ] = 0x00;
+ data [ (mms->num_stream_ids-1) * 6 + 3 ] = 0x20;
+ data [ (mms->num_stream_ids-1) * 6 + 4 ] = 0xac;
+ data [ (mms->num_stream_ids-1) * 6 + 5 ] = 0x40;
+ data [ (mms->num_stream_ids-1) * 6 + 6 ] = 0x02;
+
+ mms_send_packet ( mms, MMS_CMD_STREAM_SELECT, mms->num_stream_ids, 0xFFFF | mms->stream_ids[0] << 16, (mms->num_stream_ids-1)*6+2+4, data );
+ }
+ else
+ {
+ mms_send_packet ( mms, MMS_CMD_STREAM_SELECT, mms->num_stream_ids, 0xFFFF | mms->stream_ids[0] << 16, (mms->num_stream_ids-1)*6+2, data );
+ }
+
+ cmd = MMS_CMD_PING;
+ while ( cmd == MMS_CMD_PING )
+ {
+ cmd = mms_recv_cmd_packet ( mms->socket, &pak, &packet_len, 0, mms->quiet );
+
+ if ( cmd == MMS_CMD_PING )
+ mms_send_packet ( mms, MMS_CMD_PONG, 0, 0, 0, pak.buf );
+ }
+
+ if ( cmd == MMS_CMD_INVALID )
+ {
+ if ( !mms->quiet )
+ error ( "mms_begin_rip", "unable to get server\'s confirmation" );
+
+ return MMS_RET_ERROR;
+ }
+
+ if ( mms->stddebug != NULL )
+ mms_print_packet ( mms->stddebug, &pak, packet_len, MMS_SERVER );
+
+
+ /* ready... */
+ memset ( data, 0, 40 );
+
+ for ( i=8; i<16; ++i )
+ data[i] = 0xFF;
+
+ data[20] = 0x04;
+
+ mms_send_packet ( mms, MMS_CMD_START_PACKET, 1, 0xFFFF | mms->stream_ids[0] << 16, 24, data );
+
+ return MMS_RET_SUCCESS;
+}
+
+
+/*
+ * Writes a media packet.
+ * Returns the amount of bytes written, 0 if EOF, MMS_RET_NO_AUTH if streaming is denied, MMS_RET_ERROR either.
+ */
+ssize_t
+mms_write_stream_data ( MMS *mms )
+{
+ ssize_t len;
+ MMS_PACKET pak;
+
+ if ( mms == NULL )
+ return 0;
+
+ do
+ {
+ len = mms_recv_media_packet ( mms, &pak );
+ }
+ while ( len == MMS_RET_ACKED ); /* while we receive ACK packets */
+
+ if ( len == 0 )
+ return 0;
+ else if ( len == -1 )
+ {
+ if ( !mms->quiet )
+ error ( "mms_write_stream_data", "mms_recv_media_packet failed" );
+
+ return MMS_RET_ERROR;
+ }
+ else if ( len == MMS_RET_NO_AUTH )
+ {
+ if ( !mms->quiet )
+ error ( "mms_write_stream_data", "mms_recv_media_packet failed" );
+
+ return MMS_RET_NO_AUTH;
+ }
+
+ fwrite ( pak.buf, mms->media_packet_len, 1, mms->out ); /* we might have read less than the arbitrary media packet size */
+
+ return mms->media_packet_len;
+}
+
+
+/*
+ * Closes the connection.
+ */
+void
+mms_disconnect ( MMS *mms )
+{
+ uint8_t data [ 1024 ];
+
+ if ( mms == NULL )
+ return;
+
+ mms_send_packet ( mms, MMS_CMD_BYE_BYE, 0, 0, 0, data );
+
+ if ( ( shutdown(mms->socket, SHUT_RDWR) | close(mms->socket)) != 0 )
+ if ( !mms->quiet )
+ warning ( "mms_disconnect", "unable to close the socket properly" );
+
+ return;
+}
+
+
+/*
+ * Destroys An MMS Struct
+ */
+void
+mms_destroy ( MMS *mms )
+{
+ if ( mms == NULL )
+ return;
+
+ if ( mms->host != NULL )
+ free ( mms->host );
+
+ if ( mms->path != NULL )
+ free ( mms->path );
+
+ free ( mms );
+
+ return;
+}
diff --git a/src/mmsrip/mms.h b/src/mmsrip/mms.h
new file mode 100644
index 0000000..600d7bd
--- /dev/null
+++ b/src/mmsrip/mms.h
@@ -0,0 +1,129 @@
+/*
+ * $RCSfile: mms.h,v $
+ * $Date: 2006/01/23 20:30:43 $ - $Revision: 1.17 $
+ *
+ * This file is distributed as a part of MMSRIP ( MMS Ripper ).
+ * Copyright (c) 2005-2006 Nicolas BENOIT
+ *
+ * It is highly based on the work of SDP Multimedia and Major MMS.
+ * They deserve all the credits for it.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef __MMS_H__
+#define __MMS_H__
+
+#ifdef _WIN32
+typedef signed long int ssize_t;
+#define bcopy(s, d, l) memcpy(d, s, l)
+#include <assert.h>
+#define close closesocket
+#define read(soc, data, len) recv(soc, data, len, 0)
+#define write(soc, data, len) send(soc, data, len, 0)
+#define SHUT_RDWR SD_BOTH
+#endif
+
+#if defined(__CYGWIN__) || defined(_WIN32)
+typedef unsigned char uint8_t;
+#ifndef __uint32_t_defined
+#define __uint32_t_defined
+typedef unsigned int uint32_t;
+#endif
+typedef unsigned long long int uint64_t;
+#else
+#if defined(SOLARIS) || defined(sun)
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#endif
+
+#define MMS_SERVER 0
+#define MMS_CLIENT 1
+
+#define MMS_NO_LIVE 0
+#define MMS_LIVE 1
+
+#define MMS_WMV 0
+#define MMS_ASF 1
+
+#define MMS_TRICK_DISABLED 0
+#define MMS_TRICK_ENABLED 1
+
+#define MMS_CMD_INVALID -1
+#define MMS_CMD_HELLO 0x01
+#define MMS_CMD_PROTOCOL_SELECT 0x02
+#define MMS_CMD_FILE_REQUEST 0x05
+#define MMS_CMD_READY_TO_STREAM 0x05
+#define MMS_CMD_STREAM_INFOS 0x06
+#define MMS_CMD_START_PACKET 0x07
+#define MMS_CMD_STOP_STREAM 0x09
+#define MMS_CMD_BYE_BYE 0x0D
+#define MMS_CMD_HEADER_DATA 0x11
+#define MMS_CMD_HEADER_REQUEST 0x15
+#define MMS_CMD_NET_TESTING 0x15
+#define MMS_CMD_PING 0x1B
+#define MMS_CMD_PONG 0x1B
+#define MMS_CMD_END_OF_STREAM 0x1E
+#define MMS_CMD_STREAM_SELECT_ACK 0x21
+#define MMS_CMD_STREAM_SELECT 0x33
+
+#define MMS_RET_SUCCESS 0
+#define MMS_RET_ERROR -1
+#define MMS_RET_NO_AUTH -2
+#define MMS_RET_ACKED -3
+
+
+#define MMS_BUF_SIZE 102400
+
+typedef struct
+{
+ uint8_t buf[MMS_BUF_SIZE];
+ int num_bytes;
+} MMS_PACKET ;
+
+
+typedef struct
+{
+ char *host;
+ char *path;
+ int socket;
+ FILE *out;
+ FILE *stddebug;
+ ssize_t media_packet_len;
+ uint64_t expected_file_size;
+ int is_live;
+ int stream_type;
+ int seq_num;
+ int num_stream_ids;
+ int stream_ids[20];
+ int quiet;
+ int trick;
+} MMS ;
+
+
+MMS * mms_create ( const char *, FILE *, FILE *, const int, const int );
+int mms_connect ( MMS* );
+int mms_handshake ( MMS * );
+ssize_t mms_write_stream_header ( MMS * );
+int mms_begin_rip ( MMS * );
+ssize_t mms_write_stream_data ( MMS * );
+void mms_disconnect ( MMS * );
+void mms_destroy ( MMS * );
+
+#endif