Publisher payments and browsers like Brave aren't the answer and may hurt long term

As you may have noticed, I don't really have any ads on this site… or at least I didn't when I wrote this, but if you're reading this in the far future I probably sold out already.

Many blogs have way too many damn ads (this one too), talking ads, tons of garbage in the way all the time, making your computer go slow with god awful JavaScript and even more terrible Flash video or needlessly super high 4K HTML5 video.

You complain and nobody listens, they just say "that doesn't happen on my computer!"

Liar.

So there are ad blockers, but that hurts publishers because there's no ads to make money.

So there are solutions like Flattr and browsers like Brave which seek to fix this problem by having users put in money and then distribute the money to the content they most like and/or consume the most.

Actually, that sounds awesome, innovative, genius even, and that's not sarcasm, it does sound really cool.

But of course there's a problem with it.

The reason ads make more money is because people are more likely to click on ads than they are to pay cash, in fact nearly no users at all will pay cash, even if it's an easy option. What do I base this on? Well, unlike many of you I interact with other human beings and don't simply work from my ultralibertarian Aeron chair thinking about the benefits of mutual exchange.

I often see people, primarily dorks from the development world, … so dorks like me, say things like "I'd pay for content if I liked it enough", well first of all these people aren't clicking ads anyway, but also it's total bullshit too. Most people will still watch commercials on broadcast TV than pay for subscription services (which often still have commercials these days), in fact the vast majority of people will do this… so suddenly you'll have an influx of people paying for your site about Magento hacks?

Give me a break, dude.

You may make some money from donations or subscriptions, but almost always you'll make far more money with ads. Yes I'm sure some dumb asses out there will say they make more money with subscriptions and donations than they ever did with ads, but that's probably because they:

  • Have niche content consumed primarily by people like them who don't click ads.
  • Have niche content which ad companies don't want to support, or if they do support they payouts are low (e.g. pornography)
  • Are liars and they're just full of shit anyway, but think it makes them seem noble even though they may have never had ads on their site to begin with.

This whole thing reminds me of another issue, payments of software.

Remember that?

How you can be open source / free and just charge for support and accept donations!

Remember all the start ups that did that model… and then went right out of business? There are only a handful which made it work, and they all have a big brother (or sister, for the ladies out there)
like IBM or Oracle sponsoring them.

It's still an issue too, as I wrote about before in my post: The best way to support yourself being a free software developer is apparently getting another job.

And that's where this ten gallon hat of farts puts publishers in the same boat, because most people simply aren't going to donate, they aren't going to pay for the content they consume, and by blocking ads you are hurting publishers.

Some overload their sites for their clickbait articles, yes, but most sites aren't over the top and people really depend on it, it may only make $1,000 – $2,000 a month, and there's no way in hell anyone is gonna Flattr or Brave that much money for cats that look like Hitler pictures in posts about which restaurants have the best meat ball salad.

I'm sorry, it's not going to happen, and in the handful of cases it does is like suggesting playing the lottery, you're not helping.

As with the linked post above, it reeks of elitism and self-importance, but in an indirect way. If you are reading this I can gather that you:

  • Probably are a developer or web designer, or work near them in some way.
  • Probably never click ads.
  • Possibly use an adblocker.
  • Possibly have some sort of philosophy you think is in line with paying for content you like, but in reality you consume things all the time you wouldn't pay for anyway, such as simple fixes on a blog like this. Nobody's sent me money, but you cheap bastards link to my unicode and Geonames articles all the time.

So let's not break our arms trying to beat each other off, you may pay if you think it's cool enough, but even if you're there all the time, if you can get it for free, or you think maybe the publisher has slightly different politics than you, you probably won't ever pay, so don't try to pump sunshine up my skirt, I know you're full of shit.

If you asked me in public though, I'd totally lie and also say "yeah, I'd pay for content I like," but it'd be a lie, just like when you say it.

I do think services like Flattr and software like Brave are really cool and I want to somehow build a world where we can have those and good ads. I'm not just complaining, I do have some ideas in mind, but I lack experience in advertising so they could be absolutely terrible ideas, like have a browser or extension which does the following:

  • White listing only approved ad companies; make the voting democratic in some way or have multiple private companies with different motives reach a consensus.
  • Have a grey list when rules are violated, this can be done by users clicking a button near the ad to report it, and if enough do from enough places, it's automagical.
  • Rules that we can all pretty much agree on like: no talking or auto-play, no intensive CPU usage, no HD video, no attempts to hijack the browser, and probably other stuff too. Google's own rules for their advertisers aren't too bad.
  • Set a limit on ad to content ratio for each page load.

This alone will solve 99% of nonsense going on.

If you don't agree, I don't really care, but feel free to reply to express your opinion, and feel free to be as hostile as you want, because I'm going to return the favor, even if you agree with me.

Google Chrome actually stealing mundane ideas from Opera?

Note from the future: this was originally written before Opera became Chrome Jr.

A week or so ago, not sure when, Opera updated; nothing was really different, except the autosuggest / autofill items were now bold. I didn't think much of it, until Chrome updated, suddenly theirs were bold too.

What the…?

So, I went out looking for answers. One of the first things I noticed was since about May 4th, a lot of people were talking about Chrome's bold autofill lists; primarily asking how they could restyle that. I didn't find anyone talking about how Opera had done the same thing.

Granted, Opera is not really the most popular browser, and I use it for testing purposes primarily, but nevertheless this change really stands out to me. Not necessarily because one browser copied another, but because the copy is so mundane and silly. Is there some massive benefit to bold auto-fill or bold auto-suggest items?

Is it so revolutionary that when Opera updated, some Google Chrome hacker said "HOLY TOLEDO, WE GOTTA DO THAT, RIGHT NOW, PUSH IT OUT ASAP! WE CAN'T BE LEFT BEHIND! THIS IS DISRUPTIVE!"?

I've tried to find out if there's any discussion from either company regarding this, but I sure can't find it. If anyone has any information, I'd sure like to know why this was done, because it's so goofy.

Exchange and PHP, a nightmare with a solution

OK that title is not very smooth or clever, but a project I worked on several years ago was syncing Microsoft Exchange with PHP. There's a lot out there not documented, primarily because there are several problems with PHP's implementation of XML and Microsoft's over-use of namespaces and so forth. However, with some serious hacking, I've come up with some solutions.

This was initially based on some code I had found online, however it was so long ago I have not been able to figure it out so I cannot provide proper attribution. Additionally, impersonation does work, and I've seen a lot of people have a problem with this as well.

The biggest thing is it requires a lot of manual XML to actually work, you can't build the objects and expect it to work correctly, it literally will not, primarily due to PHP's SOAP class.

The primary benefit of this code is to see how I hacked around dealing with PHP's issues when it came to trying to deal with the Exchange calendar and task system.

Feel free to reply with any fixes, updates, etc.

A few things to note:

  • When I created this, it was a hackfest with very little time, so the code is not my best work at all; I mean it uses globals for god sake, and improper OOP, improper usage of PSR, classes doing far too much, etc.
  • This code was built for PHP 5.2 – 5.4, and takes no advantage of PHP 7 or anything.
  • Yes, those two above mean I am embarrassed by the quality of this code, but hiding it isn't really useful to anyone, especially because I am not using it in my project any more.
  • This will not work out of box, because it was built specifically for my project, however because it was such a nightmare and other people are struggling, I'm sharing it. It should give you a good idea of where to start and how to deal with some hiccoughs.
  • I assume you've already got the basis down, and you've got messages.xsd, Services.wsdl, and types.xsd, and all the other stuff. Basically I am just assuming you've got it working, you're just running into problems actually doing anything useful.

Usage from my job which pull down basically everything first. This is because repeating calendar entries are only listed once, so you have to pre-grab and store everything in order to deal with it later. The most important aspects are storing the ID and change key so that you can properly manage it later. If someone edits a calendar entry, the change key will be "changed," so every once in a while, depending on your desire of accuracy, you may need to resync everything, unless you find a better way to do it (if so please share).

   $Exchange = new Exchange('phpuser@mydomain.com', 'mypassword');

   $entries = $Exchange->calendarList(600000, 0, True, 'Beginning');

Here's the classes:

<?php
/****************************************
 * @author  @tonyshowoff
 * @version 1
 * @license MIT/X11
 ****************************************/

$GLOBALS['_EXCHANGE'] = array(
   'impersonate' => Null, /* Email address of account to impersonate */
   'curl'        => Null,
   'full_debug'  => False, /* This will display all traffic as well */
   'curl_debug'  => False /* Set cURL as verbose response */
);

$GLOBALS['_CONFIG'] = array(
   'debug_mode' => True,
   'exchange_cache' => '', /* Pathh which contains your Services.wsdl, etc */
);

class Exchange {
   public $lastError;
   private $client;
   private $impersonate;
   private $pass;
   private $user;
   private $wsdl;

   function __construct($user, $pass, $impersonate = Null) {
      $this->wsdl                          = $GLOBALS['_CONFIG']['exchange_cache'] . '/Services.wsdl';
      $this->user                          = $user;
      $this->pass                          = $pass;
      $this->impersonate                   = $impersonate;
      $GLOBALS['_EXCHANGE']['impersonate'] = $impersonate;
      $GLOBALS['_EXCHANGE']['curl']        = Null;
      $this->client                        = new ExchangeNTLMSoapClient($this->wsdl);
      $this->client->user                  = $this->user;
      $this->client->password              = $this->pass;
   }

   function calendarEntry($id) {
      $this->setup();

      $req_xml                       = new stdClass();
      $req_xml->ItemShape            = new stdClass();
      $req_xml->ItemShape->BaseShape = 'AllProperties';
      $req_xml->ItemIds              = new stdClass();
      $req_xml->ItemIds->ItemId      = new stdClass();
      $req_xml->ItemIds->ItemId->Id  = $id;

      try {
         $response = $this->client->GetItem($req_xml);
      } catch (Exception $e) {
         echo 'calendarEntry() exception on ID "' . $id . '": ', $e->getMessage(), "\n";
         $this->teardown();

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->GetItemResponseMessage->ResponseCode == 'NoError') {
         return $response->ResponseMessages->GetItemResponseMessage->Items->CalendarItem;
      } else {
         return False;
      }
   }

   function calendarEventAdd($subject, $start, $end, $location, $body = '', $type = 'HTML', $isallday = False) {
      /*
        $start and $end must be in ISO date format or rather 'c' in PHP date format
        This function returns an array of item details upon success and False upon failure. */

      $this->setup();

      $event_xml                                               = new stdClass();
      $event_xml->SendMeetingInvitations                       = 'SendToNone';
      $event_xml->SavedItemFolderId                            = new stdClass();
      $event_xml->SavedItemFolderId->DistinguishedFolderId     = new stdClass();
      $event_xml->SavedItemFolderId->DistinguishedFolderId->Id = 'calendar';
      $event_xml->Items                                        = new stdClass();
      $event_xml->Items->CalendarItem                          = new stdClass();
      $event_xml->Items->CalendarItem->Subject                 = $subject;
      $event_xml->Items->CalendarItem->Start                   = $start;
      $event_xml->Items->CalendarItem->End                     = $end;
      $event_xml->Items->CalendarItem->Body                    = new stdClass();
      $event_xml->Items->CalendarItem->Body->BodyType          = $type;
      $event_xml->Items->CalendarItem->Body->_                 = $body;
      $event_xml->Items->CalendarItem->IsAllDayEvent           = $isallday;
      $event_xml->Items->CalendarItem->LegacyFreeBusyStatus    = 'Busy'; // Legacy only (never use)
      $event_xml->Items->CalendarItem->Location                = $location;

      try {
         $response = $this->client->CreateItem($event_xml);
      } catch (Exception $e) {
         echo 'calendarEventAdd() exception: ', $e->getMessage(), "\n";
         $this->teardown();

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->CreateItemResponseMessage->ResponseCode == 'NoError') {
         return ['ItemId'    => $response->ResponseMessages->CreateItemResponseMessage->Items->CalendarItem->ItemId->Id,
                 'ChangeKey' => $response->ResponseMessages->CreateItemResponseMessage->Items->CalendarItem->ItemId->ChangeKey];
      } else {
         $this->lastError = $response->ResponseMessages->CreateItemResponseMessage->ResponseCode;
         log_out_error('exchange', "Calendar:\r\nError: " . print_r($this->lastError, 1) . "\r\n\r\nInitial data:\r\n" . print_r($event_xml, 1));

         return False;
      }
   }

   /**
    * Sets up strream handling. Internally used.
    *
    * @access private
    * @return void
    */
   private function setup() {
      stream_wrapper_unregister('http');
      stream_wrapper_register('http', 'ExchangeNTLMStream') or die('Failed to register protocol: 8c251e34-77e6-41ce-88da-4dbc90bc8a60');
   }

   /**
    * Tears down stream handling. Internally used.
    *
    * @access private
    * @return void
    */

   function teardown() {
      stream_wrapper_restore('http');
   }

   function calendarEventDelete($item_id, $type = 'HardDelete') {
      $this->setup();

      $DeleteItem                           = new stdClass();
      $DeleteItem->DeleteType               = $type;
      $DeleteItem->SendMeetingCancellations = 'SendToNone';
      $DeleteItem->ItemIds                  = new stdClass();
      $DeleteItem->ItemIds->ItemId          = new stdClass();
      $DeleteItem->ItemIds->ItemId->Id      = $item_id;

      try {
         $response = $this->client->DeleteItem($DeleteItem);
      } catch (Exception $e) {
         echo 'calendarEventDelete() exception on ID "' . $item_id . '": ', $e->getMessage(), "\n";
         $this->teardown();

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->DeleteItemResponseMessage->ResponseCode == 'NoError') {
         return True;
      } else {
         $this->lastError = $response->ResponseMessages->DeleteItemResponseMessage->ResponseCode;
         log_out_error('exchange', "Calendar:\r\nError: " . print_r($this->lastError, 1) . "\r\n\r\nInitial data:\r\n" . print_r($DeleteItem, 1));

         return False;
      }
   }

   function calendarList($limit = 10, $offset = 0, $full = False, $point = 'Beginning') {
      $this->setup();

      $FindItem            = new stdClass();
      $FindItem->Traversal = 'Shallow';
      $FindItem->ItemShape = new stdClass();

      if (True == $full) {
         $FindItem->ItemShape->BaseShape = 'AllProperties';
      } else {
         $FindItem->ItemShape->BaseShape            = 'IdOnly';
         $FindItem->ItemShape->AdditionalProperties = new SoapVar(
            '<ns1:AdditionalProperties>
                                <ns1:FieldURI FieldURI="calendar:CalendarItemType"/>
                                <ns1:FieldURI FieldURI="calendar:IsMeeting"/>
                                <ns1:FieldURI FieldURI="calendar:IsRecurring"/>
                                <ns1:FieldURI FieldURI="calendar:Organizer"/>
                                <ns1:FieldURI FieldURI="calendar:Start"/>
                                <ns1:FieldURI FieldURI="calendar:End"/>
                                <ns1:FieldURI FieldURI="calendar:Duration"/>
                                <ns1:FieldURI FieldURI="calendar:IsAllDayEvent"/>
                                <ns1:FieldURI FieldURI="calendar:Location"/>
                                <ns1:FieldURI FieldURI="calendar:MeetingRequestWasSent"/>
                                <ns1:FieldURI FieldURI="calendar:MyResponseType"/>
                                <ns1:FieldURI FieldURI="calendar:AppointmentSequenceNumber"/>
                                <ns1:FieldURI FieldURI="calendar:AppointmentState"/>
                                <ns1:FieldURI FieldURI="item:Categories"/>
                                <ns1:FieldURI FieldURI="item:HasAttachments"/>
                                <ns1:FieldURI FieldURI="item:ReminderDueBy"/>
                                <ns1:FieldURI FieldURI="item:ReminderIsSet"/>
                                <ns1:FieldURI FieldURI="item:ReminderMinutesBeforeStart"/>
                                <ns1:FieldURI FieldURI="item:Subject"/>
                                <ns1:FieldURI FieldURI="item:ItemClass"/>
                                <ns1:FieldURI FieldURI="item:Sensitivity"/>
                                <ns1:FieldURI FieldURI="item:DateTimeReceived"/>
                                <ns1:FieldURI FieldURI="item:Importance"/>
                                <ns1:FieldURI FieldURI="item:IsDraft"/>
                                <ns1:FieldURI FieldURI="item:DateTimeCreated"/>
                        </ns1:AdditionalProperties>', XSD_ANYXML);
      }

      $FindItem->IndexedPageItemView                     = new stdClass();
      $FindItem->IndexedPageItemView->MaxEntriesReturned = $limit;
      $FindItem->IndexedPageItemView->Offset             = $offset;
      $FindItem->IndexedPageItemView->BasePoint          = $point;

      $FindItem->ParentFolderIds                            = new stdClass();
      $FindItem->ParentFolderIds->DistinguishedFolderId     = new stdClass();
      $FindItem->ParentFolderIds->DistinguishedFolderId->Id = 'calendar';

      try {
         $response = $this->client->FindItem($FindItem);
      } catch (Exception $e) {
         return False;
      }

      if ($response->ResponseMessages->FindItemResponseMessage->ResponseCode != 'NoError') {
         $this->lastError = $response->ResponseMessages->FindItemResponseMessage->ResponseCode;

         return false;
      }

      if (empty($response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->CalendarItem)) {
         $items = False;
      } else {
         $items = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->CalendarItem;

         if (!is_array($items)) {
            $items = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items;
         }
      }

      $this->teardown();

      return $items;
   }

   function calendar_event_update($item_id, $changekey, $subject, $start, $end, $location, $body = '', $type = 'HTML', $isallday = False) {
      /*
        $start and $end must be in ISO date format or rather 'c' in PHP date format
        This function returns an array of item details upon success and False upon failure. */

      $this->setup();

      $update_xml = new stdClass();

      $update_xml->SendMeetingInvitationsOrCancellations = 'SendToNone';
      $update_xml->MessageDisposition                    = 'SaveOnly';
      $update_xml->ConflictResolution                    = 'AlwaysOverwrite';

      $update_xml->ItemChanges                     = new stdClass();
      $update_xml->ItemChanges->ItemChange         = new stdClass();
      $update_xml->ItemChanges->ItemChange->ItemId = new stdClass();

      $update_xml->ItemChanges->ItemChange->ItemId->Id = $item_id;

      $update_xml->ItemChanges->ItemChange->ItemId->ChangeKey = $changekey;

      $update_xml->ItemChanges->ItemChange->Updates = new stdClass();

      $update_xml->ItemChanges->ItemChange->Updates = new SoapVar(
         '<ns1:Updates>
                                <ns1:SetItemField>
                                        <ns1:FieldURI FieldURI="item:Subject" />
                                        <ns1:CalendarItem>
                                                <ns1:Subject>' . htmlspecialchars($subject) . '</ns1:Subject>
                                        </ns1:CalendarItem>
                                </ns1:SetItemField>
                                <ns1:SetItemField>
                                        <ns1:FieldURI FieldURI="calendar:Location" />
                                        <ns1:CalendarItem>
                                                <ns1:Location>' . htmlspecialchars($location) . '</ns1:Location>
                                        </ns1:CalendarItem>
                                </ns1:SetItemField>
                                <ns1:SetItemField>
                                        <ns1:FieldURI FieldURI="calendar:Start" />
                                        <ns1:CalendarItem>
                                                <ns1:Start>' . $start . '</ns1:Start>
                                        </ns1:CalendarItem>
                                </ns1:SetItemField>
                                <ns1:SetItemField>
                                        <ns1:FieldURI FieldURI="calendar:End" />
                                        <ns1:CalendarItem>
                                                <ns1:End>' . $end . '</ns1:End>
                                        </ns1:CalendarItem>
                                </ns1:SetItemField>
                                <ns1:SetItemField>
                                        <ns1:FieldURI FieldURI="item:Body" />
                                        <ns1:CalendarItem>
                                                <ns1:Body BodyType="HTML">' . htmlspecialchars($body) . '</ns1:Body>
                                        </ns1:CalendarItem>
                                </ns1:SetItemField>
                        </ns1:Updates>', XSD_ANYXML);

      try {
         $response = $this->client->UpdateItem($update_xml);
      } catch (Exception $e) {
         echo 'calendar_event_update() exception on ID "' . $item_id . '": ', $e->getMessage(), "\n";
         $this->teardown();

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->UpdateItemResponseMessage->ResponseCode == 'NoError') {
         return ['changekey' => $response->ResponseMessages->UpdateItemResponseMessage->Items->CalendarItem->ItemId->ChangeKey];
      } else {
         $this->lastError = $response->ResponseMessages->UpdateItemResponseMessage->ResponseCode;
         log_out_error('exchange', "Calendar:\r\nError: " . print_r($this->lastError, 1) . "\r\n\r\nInitial data:\r\n" . print_r($update_xml, 1));

         return False;
      }
   }

   function close() {
      curl_close($GLOBALS['_EXCHANGE']['curl']);

      $GLOBALS['_EXCHANGE']['impersonate'] = Null;
      $GLOBALS['_EXCHANGE']['curl']        = Null;

      if ($GLOBALS['_CONFIG']['debug_mode'] == True) {
         echo " [D] Close Exchange connection.\n";
      }
   }

   /**
    * Deletes a message in the mailbox of the current user.
    *
    * @access public
    *
    * @param ItemId $ItemId     (such as one returned by get_messages)
    * @param string $deletetype . (default: "HardDelete")
    *
    * @return bool $success (true: message was deleted, false: message failed to delete)
    */
   function delete_message($ItemId, $deletetype = "HardDelete") {
      $this->setup();

      $DeleteItem->DeleteType      = $deletetype;
      $DeleteItem->ItemIds->ItemId = $ItemId;

      $response = $this->client->DeleteItem($DeleteItem);

      $this->teardown();

      if ($response->ResponseMessages->DeleteItemResponseMessage->ResponseCode == "NoError") {
         return true;
      } else {
         $this->lastError = $response->ResponseMessages->DeleteItemResponseMessage->ResponseCode;

         return false;
      }
   }

   function getAttachment($item_id, $attachment_id) {
      // Doesn't seem to work.
      $this->setup();

      $req_xml                                = new stdClass();
      $req_xml->ItemShape                     = new stdClass();
      $req_xml->ItemShape->BaseShape          = 'AllProperties';
      $req_xml->ItemIds                       = new stdClass();
      $req_xml->ItemIds->ItemId               = new stdClass();
      $req_xml->ItemIds->ItemId->Id           = $item_id;
      $req_xml->ItemIds->ItemId->AttachmentId = $attachment_id;

      try {
         $response = $this->client->GetAttachment($req_xml);
      } catch (Exception $e) {
         echo 'getAttachment() exception on ID "' . $item_id . '": ', $e->getMessage(), "\n";
         $this->teardown();

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->GetItemResponseMessage->ResponseCode == 'NoError') {
         return $response->ResponseMessages->GetItemResponseMessage->Items->Attachment;
      } else {
         return False;
      }
   }

   function getInbox($limit = 10, $offset = 0) {
      $this->setup();

      $FindItem            = new stdClass();
      $FindItem->Traversal = 'Shallow';
      $FindItem->ItemShape = new stdClass();

      $FindItem->ItemShape->BaseShape = 'IdOnly';

      $FindItem->ItemShape->AdditionalProperties = new SoapVar(
         '<ns1:AdditionalProperties>
                                <ns1:FieldURI FieldURI="item:Subject"/>
                        </ns1:AdditionalProperties>', XSD_ANYXML);

      $FindItem->IndexedPageItemView                     = new stdClass();
      $FindItem->IndexedPageItemView->MaxEntriesReturned = $limit;
      $FindItem->IndexedPageItemView->Offset             = $offset;
      $FindItem->IndexedPageItemView->BasePoint          = 'Beginning';

      $FindItem->ParentFolderIds                            = new stdClass();
      $FindItem->ParentFolderIds->DistinguishedFolderId     = new stdClass();
      $FindItem->ParentFolderIds->DistinguishedFolderId->Id = 'inbox';

      try {
         $response = $this->client->FindItem($FindItem);
      } catch (Exception $e) {
         return False;
      }

      if ($response->ResponseMessages->FindItemResponseMessage->ResponseCode != 'NoError') {
         $this->lastError = $response->ResponseMessages->FindItemResponseMessage->ResponseCode;

         return false;
      }

      $items = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message;

      $this->teardown();

      return $items;
   }

   function itemExists($id) {
      $this->setup();

      $req_xml                       = new stdClass();
      $req_xml->ItemShape            = new stdClass();
      $req_xml->ItemShape->BaseShape = 'IdOnly';
      $req_xml->ItemIds              = new stdClass();
      $req_xml->ItemIds->ItemId      = new stdClass();
      $req_xml->ItemIds->ItemId->Id  = $id;

      try {
         $response = $this->client->GetItem($req_xml);
      } catch (Exception $e) {
         echo 'itemExists() exception on ID "' . $id . '": ', $e->getMessage(), "\n";

         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->GetItemResponseMessage->ResponseCode == 'NoError') {
         return True;
      } else {
         return False;
      }
   }

   function sendMessage($to, $subject, $content, $type = 'HTML', $save = False, $read = True) {
      /*
        This sends a message through Exchange as the user that's logged in via init.

        $to         - The email address we're sending to, can be internal or external
        $subject        - The subject
        $content        - The content, style dependent on $type
        $type         - Default is 'Text', but 'HTML' should be used for HTML emails
        $save         - Default F, T/F save to user's sent folder?
        $read         - Default F, T/F mark as read. @BUG: Doesn't seem to work, possibly missing another field?
       */

      $this->setup();

      $msg_xml = new stdClass();

      if ($save == True) {
         $msg_xml->MessageDisposition                           = 'SendOnly';
         $msg_xml->SavedItemFolderId                            = new stdClass();
         $msg_xml->SavedItemFolderId->DistinguishedFolderId     = new stdClass();
         $msg_xml->SavedItemFolderId->DistinguishedFolderId->Id = 'sentitems';
      } else {
         $msg_xml->MessageDisposition = 'SendOnly';
      }

      $msg_xml->Items                                               = new stdClass();
      $msg_xml->Items->Message                                      = new stdClass();
      $msg_xml->Items->Message->ItemClass                           = 'IPM.Note';
      $msg_xml->Items->Message->Subject                             = $subject;
      $msg_xml->Items->Message->Body                                = new stdClass();
      $msg_xml->Items->Message->Body->BodyType                      = $type;
      $msg_xml->Items->Message->Body->_                             = $content;
      $msg_xml->Items->Message->ToRecipients                        = new stdClass();
      $msg_xml->Items->Message->ToRecipients->Mailbox               = new stdClass();
      $msg_xml->Items->Message->ToRecipients->Mailbox->EmailAddress = $to;

      if ($read == True) {
         $msg_xml->Items->Message->IsRead = 'true';
      }

      try {
         $response = $this->client->CreateItem($msg_xml);
      } catch (Exception $e) {
         return False;
      }

      $this->teardown();

      if ($response->ResponseMessages->CreateItemResponseMessage->ResponseCode == 'NoError') {
         return True;
      } else {
         $this->lastError = $response->ResponseMessages->CreateItemResponseMessage->ResponseCode;
         log_out_error('exchange', "sendMessage:\r\nError: " . print_r($this->lastError, 1) . "\r\n\r\nInitial data:\r\n" . print_r($msg_xml, 1));

         return False;
      }
   }
}

class ExchangeNTLMSoapClient extends NTLMSoapClient {
   public $password = '';
   public $user = '';
}

class ExchangeNTLMStream extends NTLMStream {
   //protected $user = '';
   //protected $password = '';
}

class ImpersonationHeader {
   var $ConnectingSID;

   /*ExchangeImpersonationType and the
   ConnectingSIDType*/
   function __construct($email) {
      $this->ConnectingSID                = new stdClass();
      $this->ConnectingSID->PrincipalName = $email;
      /*
            $this->ConnectingSID = new stdClass();
            $this->ConnectingSID->PrimarySmtpAddress = $email;
      */
   }
}

class NTLMSoapClient extends SoapClient {
   function __doRequest($request, $location, $action, $version, $one_way = 0) {

      if ($GLOBALS['_EXCHANGE']['full_debug'] == True) {
         echo 'Request: ' . $request . "\n<br />";
         echo 'Location: ' . $location . "\n<br />";
         echo 'Action: ' . $action . "\n<br />";
         echo 'Version: ' . $version . "\n<br />";
         echo 'One-way: ' . $one_way . "\n<br />";
         echo 'Request: ' . print_r($request, 1);
      }

      $headers = [
         'Method: POST',
         'Connection: Keep-Alive',
         'User-Agent: PHP-SOAP-CURL',
         'Content-Type: text/xml; charset=utf-8',
         'SOAPAction: "' . $action . '"',
      ];

      // @TODO: Somehow we need to pass the $impersonate var
      //        from the exchange class to here, probably just in init
      //        or something, but we'll do this hack for now.

      if ($GLOBALS['_EXCHANGE']['impersonate'] != Null) {
         // @TODO / @HACK: Ideally we'd want to just use the SOAP system
         // built into PHP to do this, but it has a flaw which
         // will not let you properly impersonate someone.
         // Until it's fixed (been 2 years as of now) we do this.

         $xml = '<SOAP-ENV:Header>';
         $xml .= '<ns1:ExchangeImpersonation>';
         $xml .= '<ns1:ConnectingSID>';
         $xml .= '<ns1:PrimarySmtpAddress>' . $GLOBALS['_EXCHANGE']['impersonate'] . '</ns1:PrimarySmtpAddress>';
         $xml .= '</ns1:ConnectingSID>';
         $xml .= '</ns1:ExchangeImpersonation>';
         $xml .= '</SOAP-ENV:Header>';

         $request = str_replace('><SOAP-ENV:Body', '>' . $xml . '<SOAP-ENV:Body', $request);
      }

      $this->__last_request_headers = $headers;
      $GLOBALS['_EXCHANGE']['curl'] = curl_init($location);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_RETURNTRANSFER, true);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_HTTPHEADER, $headers);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_POST, true);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_POSTFIELDS, $request);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_USERPWD, $this->user . ':' . $this->password);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_SSL_VERIFYHOST, false);
      curl_setopt($GLOBALS['_EXCHANGE']['curl'], CURLOPT_VERBOSE, $GLOBALS['_EXCHANGE']['curl_debug']);
      $response = curl_exec($GLOBALS['_EXCHANGE']['curl']);

      if ($GLOBALS['_EXCHANGE']['full_debug'] == True) {
         echo 'Response: ' . print_r($response, 1);
      }

      // @TODO: If there's an error we need to log it, but
      //        this may need to be done at all the functions
      //        which parse this, rather than here. Though we
      //        should still log all CURL errors.

      return $response;
   }

   function __getLastRequestHeaders() {
      return implode("n", $this->__last_request_headers) . "n";
   }
}

class NTLMStream {
   private $buffer;
   private $mode;
   private $opened_path;
   private $options;
   private $path;
   private $pos;

   public function stream_close() {
      echo "[NTLMStream::stream_close] n";
      curl_close($this->ch);
   }

   public function stream_eof() {
      echo "[NTLMStream::stream_eof] ";
      if ($this->pos > strlen($this->buffer)) {
         echo "true n";

         return true;
      }
      echo "false n";

      return false;
   }

   public function stream_flush() {
      echo "[NTLMStream::stream_flush] n";
      $this->buffer = null;
      $this->pos    = null;
   }

   public function stream_open($path, $mode, $options, $opened_path) {
      echo "[NTLMStream::stream_open] $path , mode=$mode n";
      $this->path        = $path;
      $this->mode        = $mode;
      $this->options     = $options;
      $this->opened_path = $opened_path;
      $this->createBuffer($path);

      return true;
   }

   private function createBuffer($path) {
      if ($this->buffer) {
         return;
      }
      echo "[NTLMStream::createBuffer] create buffer from : $pathn";
      $this->ch = curl_init($path);
      curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
      curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
      curl_setopt($this->ch, CURLOPT_USERPWD, $this->user . ':' . $this->password);
      echo $this->buffer = curl_exec($this->ch);
      echo "[NTLMStream::createBuffer] buffer size : " . strlen($this->buffer) . "bytesn";
      $this->pos = 0;
   }

   /* return the position of the current read pointer */

   public function stream_read($count) {
      echo "[NTLMStream::stream_read] $count n";
      if (strlen($this->buffer) == 0) {
         return false;
      }
      $read = substr($this->buffer, $this->pos, $count);
      $this->pos += $count;

      return $read;
   }

   public function stream_stat() {
      echo "[NTLMStream::stream_stat] n";
      $this->createBuffer($this->path);
      $stat = [
         'size' => strlen($this->buffer),
      ];

      return $stat;
   }

   public function stream_tell() {
      echo "[NTLMStream::stream_tell] n";

      return $this->pos;
   }

   public function stream_write($data) {
      echo "[NTLMStream::stream_write] n";
      if (strlen($this->buffer) == 0) {
         return false;
      }

      return true;
   }

   /* Create the buffer by requesting the url through cURL */

   public function url_stat($path, $flags) {
      echo "[NTLMStream::url_stat] n";
      $this->createBuffer($path);
      $stat = [
         'size' => strlen($this->buffer),
      ];

      return $stat;
   }
}

class UpdateResponse {

}

?>

The best way to support yourself being a free software developer is apparently getting another job

In this YouTube video there's a crappy interview with Richard Stallman where he does his typical thing of laying out his philosophy and answering some asinine questions from the hosts. One good question asked though was, more or less, regarding the idea of supporting a family while at the same time giving your software away for free.

Free Software people love telling you that on the one hand you should be able to/you can make money with your software, but on the other they also tend to admit you can't actually make a decent living giving your stuff away for free, and yes I know you can sell your software, but if it comes with source and someone redistributes it to the world, well then you've given it away, so your attempts to charge don't really work.

Yes, there's Red Hat and a few others out there, but since those original big boys which have been heavily propped up by companies which create proprietary software and hardware, there really haven't been any other success stories. All those companies like Cygnus, VA Linux, etc are all gone. The idea of "well we can give it away and charge for support" and "we charge for manuals / physical copies of software" aren't decent and workable business models. They're great ways to go out of business though, unless you've got a big daddy company to back you up, like IBM, which doesn't play be the same rules you claim to.

Here is the part of the interview I'm talking about:

Host: Are you actuality putting the needs of have no non-free software above the needs of feeding my kid?

Stallman: Absolutely. I don't see much difference what your saying and what a thief or swindler would say trying to justify what his doing; the most effective thing an American can do not reduce ecological impact is not have a kid.

In a way that speaks for itself, but the comments too really are interesting because they often both admit the obvious issue of working essentially for free (save a few examples out of tens of thousands of FOSS projects) and at the same time claiming you'll actually be better off and make more money this way.

Not to mention that freedom, apparently, is taking freedom from everyone else; then again this is coming from zealots who think Stallman deserves GNU tacked on to the start of Linux, a requirement which no other software project, platform, or library is morally obligated to make, just Linux.

I think it may be obvious I'm not one of those who says "Richard Stallman deserves praise for all he's done," he gets more than enough praise. I'm not against open source or free software, in fact I've distributed code on this blog using the MIT/X11 license. I am against forcing everyone else to live by your rules and licensing.

If you can make your way through the typical ass kissing Richard Stallman posts, you can find the nuggets I'm talking about.

This was ridiculously stupid. I don't care if your child eats – that responsibility is on you. And I'm not willing to sacrifice my freedom, just so that you can meet your responsibility. Think about it.

It doesn't seem to occur to him that maybe I have the freedom to create closed source software, and he has the freedom to not buy it. It's as if it's imposed upon him. Maybe he should think about that.

As far as the double think goes when it comes to making money. Well, Free software apparently makes it to where developers keep the money or maybe the users, who knows:

…with this model there would no need for publishers at all. All profits would go to developers, the people who deserve the money and did the work. No need to spend on marketing or distribution either, ergo no need for publisher.

Free software is important. Free software takes the power of the CEO or director of a company and distributes it to every single user of the application. Free software is about transparency, and the ability to be less ignorant to malicous code inside software.

However, when reality sets in:

Feeding the kid, lol. As if anyone would have to starve in a western country. Here in central Europe, you certainly don't need work at all for this, there is basic coverage.

Someone's clearly never been poor.

Regardless, the point I'd like to make here is that being a software developer does not mean you are incapable of doing any other sort of work and are a charity case if you can't find a job that meets your standards.

So on the one hand as the developer you'll reap the rewards of all your hard work, but on the other you won't really have any money and "lol fuck you and your kids, get another job, moron," and they apparently don't see the connection here.

There you have it, yes you can make a living and feed your family (as if children are the only dependencies people can have, then again I guess many of these people live with their parents) but really only if you're lucky enough to work at Red Hat or GNOME, or you get a job working some place else.

I definitely see this approach as the best way to increase software investment and development. Everyone knows you do your best work, not when secure in the knowledge you can work all night getting that bug fixed and be proud, … but instead when you gotta get in bed by 8pm because you've got to be up early enough to start up the deep fryers at work; hopefully tonight you'll be able to get on that issue someone reported five months ago.

Is PHP out of fashion?

One thing I keep running into is the claim that "PHP is out of fashion," which I don't quite understand considering PHP is the most popular server-side language on the entire Internet, and many of the top websites in the world use it. Indeed, even on Twitter recently I had this conversation (edited for readability, see link for context):

Faizan Javed (‏@faizanj): A bigger issue – what is it with valley startups and PHP?
Tony Showoff ‏(@TonyShowoff): What do you mean? The polarisation of it, to where either it's evil or it's the only thing used?
Faizan Javed (‏@faizanj): the intense focus and controversy over an arguably out-of-fashion language.
Tony Showoff ‏(@TonyShowoff): Java is also "out of fashion" but still used, PHP is used by more web sites than any other language. I think it's inadequacy. A lot of things go in and out of fashion, but fashion doesn't reflect usefulness. Remember the coming p2p/push/xml/etc revolutions?
Faizan Javed (‏@faizanj): one can argue Cobol and Fortran are also still useful in their domains. But hip, cool and mainstream they are not.
Tony Showoff ‏(@TonyShowoff): So is hand looming one could say, but half of all internet sites don't use COBOL and half of looms aren't hand driven.

Like underwear, PHP is becoming cleaner, as if it's been washed with Tempa-Cheer on double rinse at high heat.

I think he does bring up a good point and question though. Are COBOL and Fortran fashionable at all since they still are in use, mostly in the realm of maintenance? Well, maybe, but I don't think so. As I tried to point out as best I could on Twitter, niche use cases are not the same thing as something being ubiquitous. Just as you can still find handloomers that doesn't mean machine looming is falling out of fashion, despite the rise in custom hand made items on etsy.com

I think people often confuse what's cool with what's in fashion and what's useful or available. This is less of a big deal in pop culture trends, but in the computer world it doesn't really make much sense. Sure, PHP may not be cool, I'm not sure if it ever was, but that doesn't change the fact that to this day when you want to find a web host, almost always they have PHP hosting available and not much, if anything else. Despite Python and Ruby becoming more hip, along with Erlang and the less useful other things which are some goofy spin off of another thing, they simply aren't available everywhere or ubiquitous.

So, in a sense, asking whether or not PHP is in fashion is sort of like asking whether or not underwear is in fashion. Sure it may be cool, or sexy, not to wear it, but for the most part you'll find it everywhere you look. Is that a bad analogy for PHP? Maybe, but reflecting PHP's problems over the years, I think it's pretty apt, but like underwear, PHP is becoming cleaner, as if it's been washed with Tempa-Cheer on double rinse at high heat.

But what about the numbers (click image for source information)?

stats-w3techs

php-trend-201301-netcraft

And finally, what about as far as community help goes? After all, a programming language's success and usability these days often relies on thriving communities. Well…

tiobe-community-stats

Uncool? Maybe, but no programming language has ever been cool. Out of fashion? I don't think so.