YouTube API 2.0 resumable upload - error 500 - ios

I'm using YouTube API 2.0 in iOS app and I have made request like this:
POST http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads
Authorization = "Bearer <acess_token>";
"Content-Length" = 516;
"Content-Type" = "application/atom+xml; charset=UTF-8";
"GData-Version" = 2;
Slug = "testVideo.mov";
"X-GData-Key" = "key=<api_key>";
with body:
<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:yt="http://gdata.youtube.com/schemas/2007">
<media:group>
<media:title type="plain">My title</media:title>
<media:description type="plain">My description</media:description>
<media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Entertainment</media:category>
<media:keywords>mediawork</media:keywords>
</media:group>
</entry>
But the server response was with error 500 and no XML body with explanatoin. What should I do? It worked a month ago.

Thanks for reporting this issue. But per our blog post, Stack Overflow prides itself in being a destination for questions and answers, and not a place to file bug reports or feature requests. We ask that developers do not post on Stack Overflow for those sorts of things, and instead make use of the templates we have for filing a bug or filing a feature request in our existing public issue tracker. (Be sure to check the list of existing open issues before filing a duplicate.)
This issue is already reported https://code.google.com/p/gdata-issues/issues/detail?id=4396
You can track the status from there.

Related

PHP library for Office365 EWS with GetRooms

Whole last week i struggled with Php libraries for EWS. I tried Package365Ews and Php-ews but both of them are missing core feature for me, or it's not documented - GetRooms. Do anyone know how to handle it, or know another library implementing this?
I personally would suggest my own library, garethp/php-ews.
It's got simple usage, but not everything is covered under simpler API's. EWS is a large thing, and documenting everything would be intense. That being said, I can certainly help you translate existing documentation by Microsoft to using this code. And, if you find yourself with more issues after this post, I check my Github daily, so logging an issue against my repository will get more help in a better place for a back and forth.
But first, let me outlay how to perform functions that aren't directly documented. Like GetRooms. My API wraps around EWS, it doesn't block your access to it. So even though I've made no obvious way to do a GetRooms, it's still there. Like this
<?php
use garethp\ews\API;
use garethp\ews\API\Type;
$api = API::fromUsernameAndPassword($server, $username, $password);
//Build Request
$result = $api->getClient()->GetRooms($request);
var_dump($result);
So, the question becomes, how do we build the request? Well, thankfully EWS is very well documented in XML. First, find the article that describes what you're trying to do, then look for the XML. I'm not 100% what you want to do, but I'll use this article as a base. The XML that we're going to try to replicate is
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010" />
</soap:Header>
<soap:Body>
<m:GetRooms>
<m:RoomList>
<t:EmailAddress>bldg3rooms#contoso.com</t:EmailAddress>
</m:RoomList>
</m:GetRooms>
</soap:Body>
</soap:Envelope>
You can skip the header, and the <m:GetRooms> part, those are built for you. What we're focused on is the payload you want to send, which is
<m:RoomList>
<t:EmailAddress>bldg3rooms#contoso.com</t:EmailAddress>
</m:RoomList>
We want to make our request look like that. So, in our code, our request will look like:
$request = array (
'RoomsList' => array (
'EmailAddress' => 'bldg3rooms#contoso.com'
)
);
$request = Type::buildFromArray($request);
And this will be translated to XML for you for the SOAP call. Using this method, for any functions that aren't documented or outright supported, you can easily still use them and just refer to the official Microsoft documentation for any request you need to make

Quickbooks v3 query url returning Error

First - my question:
When accessing the Quickbooks API, v3 (as has been forced on me as of this weekend by Intuit) I am trying to access Journal Entries (but the following problem persists across any other query) and I'm trying to use the prescribed query?query=SELECT * FROM JournalEntry (what?).
https://qb.sbfinance.intuit.com/v3/company/<id>/query?query=SELECT * FROM JournalEntry
I get as result:
{"Fault":{"Error":[{"Message":"message=Exception authenticating OAuth; errorCode=003200; statusCode=401","code":"3200"}],"type":"AUTHENTICATION"},"requestId":"6f5e5f14af7d4867ad0d8f639ade7d04","time":"2013-11-12T16:10:44.724Z"}
Which, yes, tells me that there was an error with authentication. However, when I access a URL that doesn't include this ridiculous query syntax, everything works fine:
https://qb.sbfinance.intuit.com/v3/company/<id>/journalentry/<id>
I had a similar error when accessing the v2 API, and that was bad formatting on my part, but I don't see what's wrong with my query.
And because my code for generating the authentication tokens etc is identical for both types of request, I doubt that the problem is with how I'm authenticating. Similarly "exception" tells me that there's something going wrong that the API isn't identifying. Probably a formatting of the URL that is going wrong.
I've tried replacing the query URL spaces with both a '+' and a '%20', which returns the same error.
I'm using python and rauth. The code works fine for v2 (but that was deprecated over the weekend without warning, and now is no longer documented).
As a bonus, and because apparently this is Intuit's primary mode of communication with their clients: I'm shocked that Intuit no longer has private support tickets available on their website, and that they rely on a community environment like SO to provide support. The least they could do is provide their own support. Especially if we're paying for use of the API. This is absolutely shocking.
On top of that, the API returns inconsistent responses (the same request will return an error or a valid result, depending on... no change at all). An error I have reported through their support tickets, and they have duly ignored.
Oh, and the documentation says to use
https://quickbooks.api.intuit.com/v3/v3/company/companyID/query?query=selectStmt
while the API Explorer uses:
https://qb.sbfinance.intuit.com/v3/company/<id>/query?query=SELECT * FROM JournalEntry
Anyone know which one I should actually use?
Edit
For the response that is failing, my request headers are:
{
'Content-Length': u'62',
'Accept-Encoding': 'gzip,
deflate,
compress',
'accept': 'application/json',
'User-Agent': 'python-requests/1.2.3CPython/2.7.5Darwin/13.0.0',
'Content-Type': 'application/x-www-form-urlencoded',
'authorization': 'OAuthrealm="<companyId>",
oauth_nonce="3ad98c5f71bc9f102cc31ac9815cb6d08994454e",
oauth_timestamp="1384280420",
oauth_consumer_key="<consumerKey>",
oauth_signature_method="HMAC-SHA1",
oauth_version="1.0",
oauth_token="<oauthToken>",
oauth_signature="<oauthSignature"'
}
My url is:
https://quickbooks.api.intuit.com/v3/company/<id>/query?query=SELECT+*+FROM+JournalEntry&
And my response headers are:
{'content-length': '227', 'server': 'Apache/2.2.22 (Unix)', 'connection': 'close', 'date': 'Tue, 12 Nov 2013 18:20:20 GMT', 'content-type': 'application/json;charset=ISO-8859-1', 'www-authenticate': 'OAuth oauth_problem="signature_invalid"'}
My signature hashing function is correct. It's the standard function used by Rauth, and works fine for more standard API calls (that don't have spaces or SQL select queries in them).
Pass the URL to your HTTP call without encoding:
URL = https://quickbooks.api.intuit.com/v3/company/123456789/query?query="Select * from Customer"
But to build the signature, separate the parameters from the URL, then encode separately, you should get:
"GET" + "&" +
URLEncode(https://quickbooks.api.intuit.com/v3/company/123456789/query) + "&" +
URLEncode(query=Select%20%2A%20from%20Customer), where Select%20%2A%20from%20Customer is the encoding of Select * from Customer
Note the SQL gets encoded a second time, when generating the signature.
Et voila ! I spent a week on this, I know what I'm talking about.
(notations are from VBA language, so replace as appropriate)
It turns out that the actual problem is that the Quickbooks documentation is wrong as of this writing (2013/11/14).
The documentation says that the query URL expects a GET request, which is not the case. This works when submitting SELECT statement as part of the body of a POST request.
See here for more details: https://intuitpartnerplatform.lc.intuit.com/questions/786661-python-script-to-integrate-with-quickbook
I had tried this API call using Java devkit.
JournalEntry je = GenerateQuery.createQueryEntity(JournalEntry.class);
String jeQuery = select($(je)).generate();
System.out.println("Query - " + jeQuery);
QueryResult JournalEntryRes = service.executeQuery(jeQuery);
Request URI : https://quickbooks.api.intuit.com/v3/company/688779980/query?query=SELECT+*+FROM+JournalEntry&
Response XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2013-11-12T09:50:39.836-08:00">
<QueryResponse startPosition="1" maxResults="1" totalCount="1">
<JournalEntry domain="QBO" sparse="false">
<Id>22734</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2013-10-15T08:42:12-07:00</CreateTime>
<LastUpdatedTime>2013-10-15T08:42:12-07:00</LastUpdatedTime>
</MetaData>
<TxnDate>2013-10-15</TxnDate>
<Line>
<Id>0</Id>
<Amount>100.00</Amount>
<DetailType>JournalEntryLineDetail</DetailType>
<JournalEntryLineDetail>
<PostingType>Debit</PostingType>
<AccountRef name="Advertising">9</AccountRef>
</JournalEntryLineDetail>
</Line>
<Line>
<Id>1</Id>
<Amount>100.00</Amount>
<DetailType>JournalEntryLineDetail</DetailType>
<JournalEntryLineDetail>
<PostingType>Credit</PostingType>
<AccountRef name="Advertising">9</AccountRef>
</JournalEntryLineDetail>
</Line>
<Adjustment>false</Adjustment>
</JournalEntry>
</QueryResponse>
</IntuitResponse>
You can try this call from V3 QBO ApiExplorer as well.
Query - SELECT * FROM JournalEntry
Thanks
you need to encode the query, but not the whole url
https://quickbooks.api.intuit.com/v3/company/123456789/query?query=" & URLEncode("Select * from Customer")
see sample explained here :
https://developer.intuit.com/docs/0100_quickbooks_online/0300_references/0000_programming_guide/0050_data_queries

DotNetOpenAuth LinkedIn post share error using Asp.Net MVC

DotNetOpenAuth is an amazing package. However, I keep hitting some hurdle or other. Till now most of them got solved, now I have hit a problem, which is taking up a lot of my time.
The Problem:
I want to share a post on linkedin using DotNetOpenAuth. I was able to share a few posts a while back, but all of a sudden things are now broken, and I am not able to post shares anymore. I do not recollect making much changes in my code. Can someone have a look at my diagnosis, code and see if I am messing something up?
The Diagnosis:
I ran fiddler while making requests via my code and via LinkedIn's Rest Console. Here's the Fiddler output for both scenarios:
Fiddler Output for request that was made via Rest Console provided by linkedin
POST /v1/people/~/shares HTTP/1.1
Authorization: OAuth oauth_consumer_key="{consumer_key}",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1362258084",oauth_nonce="-141812272",oauth_version="1.0",oauth_token="{access_token}",oauth_signature="HBMUfvHYJAxz%2BszxStVJ%2BhfQPEQ%3D"
Host: api.linkedin.com
Content-Length: 645
X-Target-URI: http://api.linkedin.com
Content-Type: application/xml
Connection: Keep-Alive
<?xml version="1.0" encoding="UTF-8"?>
<share>
<comment>test comment</comment>
<content>
<title>test title</title>
<submitted-url>{my_test_url}</submitted-url>
<submitted-image-url>{my_image_url}</submitted-image-url>
</content>
<visibility>
<code>anyone</code>
</visibility>
</share>
Fiddler output for request that was made via my code(using DNOA):
POST http://api.linkedin.com/v1/people/~/shares HTTP/1.1
Authorization: OAuth oauth_token="{access_token}",oauth_consumer_key="{my_consumer_key}",oauth_nonce="ECYFLc0l",oauth_signature_method="HMAC-SHA1",oauth_signature="FobR745YkMlmBpOqoTDr8CwGZIQ%3D",oauth_version="1.0",oauth_timestamp="1362263560"
Content-Type: application/xml
Host: api.linkedin.com
Content-Length: 510
Connection: Keep-Alive
<?xml version="1.0" encoding="UTF-8"?>
<share>
<comment>test comment</comment>
<content>
<title>test title</title>
<description>test description</description>
<submitted-url>{my_test_url}</submitted-url>
<submitted-image-url>{my_image_url}</submitted-image-url>
</content>
<visibility>
<code>anyone</code>
</visibility>
</share>
More details:
I know that I need to pass scope while performing oauth authentication, so already have this in place. I also get proper permission prompt that reads "NETWORK UPDATES Retrieve and post updates to LinkedIn as you" when user is authorizing my application
Do note that I do get an access token, and I am able to perform some read operations, like for getting user details using this token, however, I am not able to use the token to share posts on LinkedIn. Currently the implementation is using LinkedIn oAuth 1.0a.
Here's my code block as well:
public bool PostToLinkedIn(WebConsumer consumer, string sShareMessageXml, string accessToken = null)
{
var payload = Encoding.ASCII.GetBytes(sShareMessageXml);
AuthorizedTokenResponse token = null;
bool bIsAccessTokenSupplied = !string.IsNullOrEmpty(accessToken);
if (!bIsAccessTokenSupplied)
{
token = consumer.ProcessUserAuthorization();
}
if (token != null || bIsAccessTokenSupplied)
{
this.AccessToken = bIsAccessTokenSupplied ? accessToken : token.AccessToken;
var resourceEndpoint = new MessageReceivingEndpoint("http://api.linkedin.com/v1/people/~/shares", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
HttpWebRequest request = consumer.PrepareAuthorizedRequest(resourceEndpoint, AccessToken);
request.ServicePoint.Expect100Continue = false;
request.Method = "POST";
request.ContentLength = payload.Length;
request.ContentType = "application/xml";
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(payload, 0, payload.Length);
}
var response = consumer.Channel.WebRequestHandler.GetResponse(request);
var responseData = response.GetResponseReader().ReadToEnd();
var xmlData = XDocument.Parse(responseData);
if (xmlData.Elements(XName.Get("update")).Any())
{
return true;
}
}
return false;
}
Please let me know if you need more details. Any help would be great.
I have posted a brief version of the same problem on DNOA's google group as well here. Hopefully someone has faced this issue and has a solution.

Bad Request in SOAPUI

I am attempting to consume a web service using Delphi 2010 and Indy. To establish a usable SOAP stream to compare to the one created by my program, I am testing in SOAPUI. I am using a SOAP stream provided by the web service provider which also matches the SOAP stream specified in the WSDL file. I am getting an HTTP 400 (bad request) error from the service.
From what I can find online, it appears that receiving an HTTP 400 error indicates that your SOAP request is malformed and can not be read by the web service. I have tested my SOAP stream using XMLPad and the XML seems to be well formed. I suppose this may mean that something does not match its schema requirement. I will first check the schema description for the password in case that is expected to not be sent as plain text. What else should I be checking to eliminate an HTTP 400 error?
Here is my request (less username and password) in case it helps:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://wwww3.org/2001/XMLSchema-instance">
<soap:Header>
<wsa:Action>http://edd.ca.gov/SendTransmission</wsa:Action>
<wsa:MessageID>urn:uuid:5aa788dc-86e1-448b-b085-2d2743cf9f26</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://fsettestversion.edd.ca.gov/fsetproxy/fsetservice.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken">
<wsse:Username>#USERNAME#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#PASSWORD#/wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">O5QWht1bslLCX6KnlEypAA==</wsse:Nonce>
<wsu:Created>2012-02-29T22:32:38.250Z</wsu:Created>
</wsse:UsernameToken>
<wsu:Timestamp wsu:Id="Timestamp-805a7373-335c-43b6-ba21-6596c4848dbf">
<wsu:Created>2012-02-22T15:41:42Z</wsu:Created>
<wsu:Expires>2012-02-22T15:46:42Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<SendTransmission xmlns="http://edd.ca.gov/">
<SendTransmissionRequest xmlns="http://www.irs.gov/a2a/mef/MeFTransmitterServiceWse.xsd">
<TransmissionDataList>
<Count>1</Count>
<TransmissionData>
<TransmissionId>123456789</TransmissionId>
<ElectronicPostmark>2012-02-22T07:41:42.2502206-08:00</ElectronicPostmark>
</TransmissionData>
</TransmissionDataList>
</SendTransmissionRequest>
<fileBytes>
<xop:Include href="cid:1.634654933022658454#example.org"/>
</fileBytes>
</SendTransmission>
</soap:Body>
</soap:Envelope>
There may be something else, but at the moment, I am suspicious of the wsse:UsernameToken. I downloaded the document at http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf and read it last night. It's written in fairly plain language and I feel like I understand what it is saying but it leaves me with a smaller question than the one I asked originally. This document proposes that you can use a plain text password in this format:
<S11:Envelope xmlns:S11="..." xmlns:wsse="...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>Zoe</wsse:Username>
<wsse:Password>IloveDogs</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
Or you can use a password digest. It defines a password digest like this:
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
According to the reference, the format for a password digest would look like this:
<S11:Envelope xmlns:S11="..." xmlns:wsse="..." xmlns:wsu= "...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>NNK</wsse:Username>
<wsse:Password Type="...#PasswordDigest">
weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==
</wsse:Password>
<wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>
<wsu:Created>2003-07-16T01:24:32Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
This is not the format used in the example provided by the web service publisher. The plain text version in the reference does not use a nonce. The example message uses a nonce but calls for a plain text password. It appears to me that the use of a nonce without a password digest does not add any security to the message. It could be any random string of characters if there is no agreement for how it is to be created. Am I missing the point?
I know this must seem like a tedious undertaking, but I am hoping that by providing this here, maybe we can provide a little help to the next person coming along.
I too have come across this issue. The web service publisher (edd.ca.gov) responded by stating that the " value is required by the SOAP 1.2 standards" yet I find no valid support for that. It looks like we both are heading down the same path (FSET) and maybe we should team up and work together, two heads are better than one. I have found many mistakes within the example code and I too have yet get it to work.

Microsoft Translator API answers 500 internal server error

I'm trying to use Microsoft's Translator API in my Rails app. Unfortunately and mostly unexpected, the server answers always with an internal server error. I also tried it manually with Poster[1] and I get the same results.
In more detail, what am I doing? I'm creating an XML string which goes into the body of the request. I used the C# Example of the API documentation. Well, and then I'm just invoking the RESTservice.
My code looks like this:
xmlns1 = "http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"
xmlns2 = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"
xml_builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.TranslateArrayRequest("xmlns:ms" => xmlns1, "xmlns:arr" => xmlns2) {
xml.AppId token #using temporary token instead of appId
xml.From source
xml.To target
xml.Options {
xml["ms"].ContentType {
xml.text "text/html"
}
}
xml.Texts {
translate.each do |key,val|
xml["arr"].string {
xml.text CGI::unescape(val)
}
end
}
}
end
headers = {
'Content-Type' => 'text/xml'
}
uri = URI.parse(##msTranslatorBase + "/TranslateArray" + "?appId=" + token)
req = Net::HTTP::Post.new(uri.path, headers)
req.body = xml_builder.to_xml
response = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
# [...]
The xml_builder produces something like the following XML. Differently to the example from the API page, I'm defining two namespaces instead of referencing them on the certain tags (mainly because I wanted to reduces the overhead) -- but this doesn't seem to be a problem, when I do it like the docu-example I also get an internal server error.
<?xml version="1.0" encoding="UTF-8"?>
<TranslateArrayRequest xmlns:ms="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<AppId>TX83NVx0MmIxxCzHjPwo2_HgYN7lmWIBqyjruYm7YzCpwnkZL5wtS5oucxqlEFKw9</AppId>
<From>de</From>
<To>en</To>
<Options>
<ms:ContentType>text/html</ms:ContentType>
</Options>
<Texts>
<arr:string>Bitte übersetze diesen Text.</arr:string>
<arr:string>Das hier muss auch noch übersetzt werden.</arr:string>
</Texts>
</TranslateArrayRequest>
Every time I request the service it answers with
#<Net::HTTPInternalServerError 500 The server encountered an error processing the request. Please see the server logs for more details.>
... except I do some unspecified things, like using GET instead of POST, then it answers with something like "method not allowed".
I thought it might be something wrong with the XML stuff, because I can request an AppIdToken and invoke the Translate method without problems. But to me, the XML looks just fine. The documentation states that there is a schema for the expected XML:
The request body is a xml string generated according to the schema specified at http:// api.microsofttranslator.com/v2/Http.svc/help
Unfortunately, I cannot find anything on that.
So now my question(s): Am I doing something wrong? Maybe someone experienced similar situations and can report on solutions or work-arounds?
[1] Poster FF plugin > addons.mozilla.org/en-US/firefox/addon/poster/
Well, after lot's of trial-and-error I think I made it. So in case someone has similar problems, here is how I fixed this:
Apparently, the API is kind of fussy with the incoming XML. But since there is no schema (or at least I couldn't find the one specified in the documentation) it's kind of hard to do it the right way: the ordering of the tags is crucial!
<TranslateArrayRequest>
<AppId/>
<From/>
<Options />
<Texts/>
<To/>
</TranslateArrayRequest>
When the XML has this ordering it works. Otherwise you'll only see the useless internal server error response. Furthermore, I read a couple of times that the API also breaks if the XML contains improper UTF-8. One can force untrusted UTF-8 (e.g. coming from a user form) this way:
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
valid_string = ic.iconv(untrusted_string + ' ')[0..-2]

Resources